[install] Add reg_set_sz() function
[sanbootconf.git] / driver / ibft.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 #pragma warning(disable:4327)  /* indirection alignment mismatch */
22
23 #include <ntddk.h>
24 #include <initguid.h>
25 #include <ntstrsafe.h>
26 #include <ndis.h>
27 #include <ndisguid.h>
28 #include <ntddndis.h>
29 #include "sanbootconf.h"
30 #include "registry.h"
31 #include "ibft.h"
32
33 /**
34  * Convert IPv4 address to string
35  *
36  * @v ipaddr            IP address
37  * @ret ipaddr          IP address string
38  *
39  * This function returns a static buffer.
40  */
41 static LPSTR inet_ntoa ( ULONG ipaddr ) {
42         static CHAR buf[16];
43
44         RtlStringCbPrintfA ( buf, sizeof ( buf ), "%d.%d.%d.%d",
45                              ( ( ipaddr >> 0  ) & 0xff ),
46                              ( ( ipaddr >> 8  ) & 0xff ),
47                              ( ( ipaddr >> 16 ) & 0xff ),
48                              ( ( ipaddr >> 24 ) & 0xff ) );
49         return buf;
50 }
51
52 /**
53  * Check to see if iBFT string exists
54  *
55  * @v string            iBFT string
56  * @ret exists          String exists
57  */
58 static BOOLEAN ibft_string_exists ( PIBFT_STRING string ) {
59         return ( ( BOOLEAN ) ( string->offset != 0 ) );
60 }
61
62 /**
63  * Read iBFT string
64  *
65  * @v ibft              iBFT
66  * @v string            iBFT string
67  * @ret string          Standard C string
68  */
69 static LPSTR ibft_string ( PIBFT_TABLE ibft, PIBFT_STRING string ) {
70         if ( string->offset ) {
71                 return ( ( ( PCHAR ) ibft ) + string->offset );
72         } else {
73                 return "";
74         }
75 }
76
77 /**
78  * Check to see if iBFT IP address exists
79  *
80  * @v ipaddr            IP address
81  * @ret exists          IP address exists
82  */
83 static BOOLEAN ibft_ipaddr_exists ( PIBFT_IPADDR ipaddr ) {
84         return ( ( BOOLEAN ) ( ipaddr->in != 0 ) );
85 }
86
87 /**
88  * Convert iBFT IP address to string
89  *
90  * @v ipaddr            IP address
91  * @ret ipaddr          IP address string
92  *
93  * This function returns a static buffer, as per inet_ntoa().
94  */
95 static LPSTR ibft_ipaddr ( PIBFT_IPADDR ipaddr ) {
96         return inet_ntoa ( ipaddr->in );
97 }
98
99 /**
100  * Parse iBFT initiator structure
101  *
102  * @v ibft              iBFT
103  * @v initiator         Initiator structure
104  */
105 static VOID parse_ibft_initiator ( PIBFT_TABLE ibft,
106                                    PIBFT_INITIATOR initiator ) {
107         PIBFT_HEADER header = &initiator->header;
108
109         /* Dump structure information */
110         DbgPrint ( "Found iBFT Initiator %d:\n", header->index );
111         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
112                    ( header->flags & IBFT_FL_INITIATOR_BLOCK_VALID
113                      ? ", valid" : "" ),
114                    ( header->flags & IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED
115                      ? ", boot selected" : "" ) );
116         if ( ! ( header->flags & IBFT_FL_INITIATOR_BLOCK_VALID ) )
117                 return;
118         DbgPrint ( "  iSNS = %s\n", ibft_ipaddr ( &initiator->isns_server ) );
119         DbgPrint ( "  SLP = %s\n", ibft_ipaddr ( &initiator->slp_server ) );
120         DbgPrint ( "  Radius = %s", ibft_ipaddr ( &initiator->radius[0] ) );
121         DbgPrint ( ", %s\n", ibft_ipaddr ( &initiator->radius[1] ) );
122         DbgPrint ( "  Name = %s\n",
123                    ibft_string ( ibft, &initiator->initiator_name ) );
124 }
125
126 /**
127  * Fetch NIC MAC address
128  *
129  * @v name              NDIS device name
130  * @v device            NDIS device object
131  * @v file              NDIS file object
132  * @v mac               MAC address buffer
133  * @v mac_len           MAC address buffer length
134  * @ret ntstatus        NT status
135  */
136 static NTSTATUS fetch_mac ( PUNICODE_STRING name, PDEVICE_OBJECT device,
137                             PFILE_OBJECT file, PUCHAR mac, ULONG mac_len ) {
138         KEVENT event;
139         ULONG in_buf;
140         IO_STATUS_BLOCK io_status;
141         PIRP irp;
142         PIO_STACK_LOCATION io_stack;
143         ULONG i;
144         NTSTATUS status;
145
146         /* Construct IRP to query MAC address */
147         KeInitializeEvent ( &event, NotificationEvent, FALSE );
148         in_buf = OID_802_3_CURRENT_ADDRESS;
149         irp = IoBuildDeviceIoControlRequest ( IOCTL_NDIS_QUERY_GLOBAL_STATS,
150                                               device, &in_buf,
151                                               sizeof ( in_buf ), mac, mac_len,
152                                               FALSE, &event, &io_status );
153         if ( ! irp ) {
154                 DbgPrint ( "Could not build IRP to retrieve MAC for \"%wZ\"\n",
155                            name );
156                 return STATUS_UNSUCCESSFUL;
157         }
158         io_stack = IoGetNextIrpStackLocation( irp );
159         io_stack->FileObject = file;
160
161         /* Issue IRP */
162         status = IoCallDriver ( device, irp );
163         if ( status == STATUS_PENDING ) {
164                 status = KeWaitForSingleObject ( &event, Executive, KernelMode,
165                                                  FALSE, NULL );
166         }
167         if ( NT_SUCCESS ( status ) )
168                 status = io_status.Status;
169         if ( ! NT_SUCCESS ( status ) ) {
170                 DbgPrint ( "IRP failed to retrieve MAC for \"%wZ\": %x\n",
171                            name, status );
172                 return status;
173         }
174
175         /* Dump MAC address */
176         DbgPrint ( "Found NIC with MAC address" );
177         for ( i = 0 ; i < mac_len ; i++ )
178                 DbgPrint ( "%c%02x", ( i ? ':' : ' ' ), mac[i] );
179         DbgPrint ( " at \"%wZ\"\n", name );
180
181         return STATUS_SUCCESS;
182 }
183
184 /**
185  * Fetch NIC PDO
186  *
187  * @v name              NDIS device name
188  * @v device            NDIS device object
189  * @v pdo               Associated physical device object
190  * @ret ntstatus        NT status
191  */
192 static NTSTATUS fetch_pdo ( PUNICODE_STRING name, PDEVICE_OBJECT device,
193                             PDEVICE_OBJECT *pdo ) {
194         KEVENT event;
195         IO_STATUS_BLOCK io_status;
196         PIRP irp;
197         PIO_STACK_LOCATION io_stack;
198         PDEVICE_RELATIONS relations;
199         NTSTATUS status;
200
201         /* Construct IRP to query MAC address */
202         KeInitializeEvent ( &event, NotificationEvent, FALSE );
203         irp = IoBuildSynchronousFsdRequest ( IRP_MJ_PNP, device, NULL, 0, NULL,
204                                              &event, &io_status );
205         if ( ! irp ) {
206                 DbgPrint ( "Could not build IRP to retrieve PDO for \"%wZ\"\n",
207                            name );
208                 return STATUS_UNSUCCESSFUL;
209         }
210         io_stack = IoGetNextIrpStackLocation( irp );
211         io_stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
212         io_stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
213
214         /* Issue IRP */
215         status = IoCallDriver ( device, irp );
216         if ( status == STATUS_PENDING ) {
217                 status = KeWaitForSingleObject ( &event, Executive, KernelMode,
218                                                  FALSE, NULL );
219         }
220         if ( NT_SUCCESS ( status ) )
221                 status = io_status.Status;
222         if ( ! NT_SUCCESS ( status ) ) {
223                 DbgPrint ( "IRP failed to retrieve PDO for \"%wZ\": %x\n",
224                            name, status );
225                 return status;
226         }
227
228         /* Extract PDO */
229         relations = ( ( PDEVICE_RELATIONS ) io_status.Information );
230         *pdo = relations->Objects[0];
231
232         /* Free the relations list allocated by the IRP */
233         ExFreePool ( relations );
234
235         return STATUS_SUCCESS;
236 }
237
238 /**
239  * Fetch NetCfgInstanceId registry value
240  *
241  * @v pdo               Physical device object
242  * @v netcfginstanceid  Value to allocate and fill in
243  * @ret ntstatus        NT status
244  *
245  * The caller must eventually free the allocated value.
246  */
247 static NTSTATUS fetch_netcfginstanceid ( PDEVICE_OBJECT pdo,
248                                          LPWSTR *netcfginstanceid ) {
249         HANDLE reg_key;
250         NTSTATUS status;
251
252         /* Open driver registry key */
253         status = IoOpenDeviceRegistryKey ( pdo, PLUGPLAY_REGKEY_DRIVER,
254                                            KEY_READ, &reg_key );
255         if ( ! NT_SUCCESS ( status ) ) {
256                 DbgPrint ( "Could not open driver registry key for PDO %p: "
257                            "%x\n", pdo, status );
258                 goto err_ioopendeviceregistrykey;
259         }
260
261         /* Read NetCfgInstanceId value */
262         status = fetch_reg_sz ( reg_key, L"NetCfgInstanceId",
263                                 netcfginstanceid );
264         if ( ! NT_SUCCESS ( status ) )
265                 goto err_fetch_reg_wstr;
266
267  err_fetch_reg_wstr:
268         ZwClose ( reg_key );
269  err_ioopendeviceregistrykey:
270         return status;
271 }
272
273 /**
274  * Store IPv4 parameter into a string registry value
275  *
276  * @v reg_key           Registry key
277  * @v value_name        Registry value name
278  * @v ipaddr            IPv4 address
279  * @ret ntstatus        NT status
280  */
281 static NTSTATUS store_ipv4_parameter_sz ( HANDLE reg_key, LPCWSTR value_name,
282                                           ULONG ipaddr ) {
283         WCHAR buf[16];
284         LPWSTR value;
285
286         if ( ipaddr ) {
287                 RtlStringCbPrintfW ( buf, sizeof ( buf ),
288                                      L"%S", inet_ntoa ( ipaddr ) );
289                 value = buf;
290         } else {
291                 value = L"";
292         }
293
294         return reg_store_sz ( reg_key, value_name, value );
295 }
296
297 /**
298  * Store IPv4 parameter into a multi-string registry value
299  *
300  * @v reg_key           Registry key
301  * @v value_name        Registry value name
302  * @v ipaddr            IPv4 address
303  * @ret ntstatus        NT status
304  */
305 static NTSTATUS store_ipv4_parameter_multi_sz ( HANDLE reg_key,
306                                                 LPCWSTR value_name,
307                                                 ULONG ipaddr ) {
308         WCHAR buf[16];
309         LPWSTR value;
310
311         if ( ipaddr ) {
312                 RtlStringCbPrintfW ( buf, sizeof ( buf ),
313                                      L"%S", inet_ntoa ( ipaddr ) );
314                 value = buf;
315         } else {
316                 value = NULL;
317         }
318
319         return reg_store_multi_sz ( reg_key, value_name, value, NULL );
320 }
321
322 /**
323  * Store TCP/IP parameters in registry
324  *
325  * @v nic               iBFT NIC structure
326  * @v netcfginstanceid  Interface name within registry
327  * @ret ntstatus        NT status
328  */
329 static NTSTATUS store_tcpip_parameters ( PIBFT_NIC nic,
330                                          LPCWSTR netcfginstanceid ) {
331         LPCWSTR key_name_prefix = ( L"\\Registry\\Machine\\SYSTEM\\"
332                                     L"CurrentControlSet\\Services\\"
333                                     L"Tcpip\\Parameters\\Interfaces\\" );
334         LPWSTR key_name;
335         SIZE_T key_name_len;
336         HANDLE reg_key;
337         ULONG subnet_mask;
338         NTSTATUS status;
339
340         /* Allocate key name */
341         key_name_len = ( ( wcslen ( key_name_prefix ) +
342                            wcslen ( netcfginstanceid ) + 1 ) *
343                          sizeof ( key_name[0] ) );
344         key_name = ExAllocatePoolWithTag ( NonPagedPool, key_name_len,
345                                            SANBOOTCONF_POOL_TAG );
346         if ( ! key_name ) {
347                 DbgPrint ( "Could not allocate TCP/IP key name\n" );
348                 status = STATUS_UNSUCCESSFUL;
349                 goto err_exallocatepoolwithtag;
350         }
351
352         /* Construct key name */
353         RtlStringCbCopyW ( key_name, key_name_len, key_name_prefix );
354         RtlStringCbCatW ( key_name, key_name_len, netcfginstanceid );
355
356         /* Open key */
357         status = reg_open ( key_name, &reg_key );
358         if ( ! NT_SUCCESS ( status ) )
359                 goto err_reg_open;
360
361         /* Store IP address */
362         status = store_ipv4_parameter_multi_sz ( reg_key, L"IPAddress",
363                                                  nic->ip_address.in );
364         if ( ! NT_SUCCESS ( status ) )
365                 goto err_reg_store;
366
367         /* Store subnet mask */
368         subnet_mask = RtlUlongByteSwap ( 0xffffffffUL << ( 32 - nic->subnet_mask_prefix ) );
369         status = store_ipv4_parameter_multi_sz ( reg_key, L"SubnetMask",
370                                                  subnet_mask );
371         if ( ! NT_SUCCESS ( status ) )
372                 goto err_reg_store;
373
374         /* Store default gateway */
375         status = store_ipv4_parameter_multi_sz ( reg_key, L"DefaultGateway",
376                                                  nic->gateway.in );
377         if ( ! NT_SUCCESS ( status ) )
378                 goto err_reg_store;
379
380         /* Store DNS servers */
381         status = store_ipv4_parameter_sz ( reg_key, L"NameServer",
382                                            nic->dns[0].in );
383         if ( ! NT_SUCCESS ( status ) )
384                 goto err_reg_store;
385
386         /* Disable DHCP */
387         status = reg_store_dword ( reg_key, L"EnableDHCP", 0 );
388         if ( ! NT_SUCCESS ( status ) )
389                 goto err_reg_store;
390
391  err_reg_store:
392         reg_close ( reg_key );
393  err_reg_open:
394         ExFreePool ( key_name );
395  err_exallocatepoolwithtag:
396         return status;
397 }
398
399 /**
400  * Try to configure NIC from iBFT NIC structure
401  *
402  * @v nic               iBFT NIC structure
403  * @v name              NDIS device name
404  * @ret ntstatus        NT status
405  */
406 static NTSTATUS try_configure_nic ( PIBFT_NIC nic, PUNICODE_STRING name ) {
407         BOOLEAN must_disable;
408         PFILE_OBJECT file;
409         PDEVICE_OBJECT device;
410         UCHAR mac[6];
411         PDEVICE_OBJECT pdo;
412         LPWSTR netcfginstanceid;
413         NTSTATUS status;
414
415         /* Enable interface if not already done */
416         status = IoSetDeviceInterfaceState ( name, TRUE );
417         must_disable = ( NT_SUCCESS ( status ) ? TRUE : FALSE );
418
419         /* Get device and file object pointers */
420         status = IoGetDeviceObjectPointer ( name, FILE_ALL_ACCESS, &file,
421                                             &device );
422         if ( ! NT_SUCCESS ( status ) ) {
423                 /* Not an error, apparently; IoGetDeviceInterfaces()
424                  * seems to return a whole load of interfaces that
425                  * aren't attached to any objects.
426                  */
427                 goto err_iogetdeviceobjectpointer;
428         }
429
430         /* See if NIC matches */
431         status = fetch_mac ( name, device, file, mac, sizeof ( mac ) );
432         if ( ! NT_SUCCESS ( status ) )
433                 goto err_fetch_mac;
434         if ( memcmp ( nic->mac_address, mac, sizeof ( mac ) ) != 0 )
435                 goto err_compare_mac;
436         DbgPrint ( "iBFT NIC %d is interface \"%wZ\"\n",
437                    nic->header.index, name );
438
439         /* Get matching PDO */
440         status = fetch_pdo ( name, device, &pdo );
441         if ( ! NT_SUCCESS ( status ) )
442                 goto err_fetch_pdo;
443         DbgPrint ( "iBFT NIC %d is PDO %p\n", nic->header.index, pdo );
444
445         /* Get NetCfgInstanceId */
446         status = fetch_netcfginstanceid ( pdo, &netcfginstanceid );
447         if ( ! NT_SUCCESS ( status ) )
448                 goto err_fetch_netcfginstanceid;
449         DbgPrint ( "iBFT NIC %d is NetCfgInstanceId \"%S\"\n",
450                    nic->header.index, netcfginstanceid );
451
452         /* Store registry values */
453         status = store_tcpip_parameters ( nic, netcfginstanceid );
454         if ( ! NT_SUCCESS ( status ) )
455                 goto err_store_tcpip_parameters;
456
457  err_store_tcpip_parameters:
458         ExFreePool ( netcfginstanceid );
459  err_fetch_netcfginstanceid:
460  err_fetch_pdo:
461  err_compare_mac:
462  err_fetch_mac:
463         /* Drop object reference */
464         ObDereferenceObject ( file );
465  err_iogetdeviceobjectpointer:
466         /* Disable interface if we had to enable it */
467         if ( must_disable )
468                 IoSetDeviceInterfaceState ( name, FALSE );
469         return status;
470 }
471
472 /**
473  * Parse iBFT NIC structure
474  *
475  * @v ibft              iBFT
476  * @v nic               NIC structure
477  */
478 static VOID parse_ibft_nic ( PIBFT_TABLE ibft, PIBFT_NIC nic ) {
479         PIBFT_HEADER header = &nic->header;
480         PWSTR symlinks;
481         PWSTR symlink;
482         UNICODE_STRING u_symlink;
483         NTSTATUS status;
484
485         /* Dump structure information */
486         DbgPrint ( "Found iBFT NIC %d:\n", header->index );
487         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
488                    ( header->flags & IBFT_FL_NIC_BLOCK_VALID
489                      ? ", valid" : "" ),
490                    ( header->flags & IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED
491                      ? ", boot selected" : "" ),
492                    ( header->flags & IBFT_FL_NIC_GLOBAL
493                      ? ", global address" : ", link local address" ) );
494         if ( ! ( header->flags & IBFT_FL_NIC_BLOCK_VALID ) )
495                 return;
496         DbgPrint ( "  IP = %s/%d\n", ibft_ipaddr ( &nic->ip_address ),
497                    nic->subnet_mask_prefix );
498         DbgPrint ( "  Origin = %d\n", nic->origin );
499         DbgPrint ( "  Gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
500         DbgPrint ( "  DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
501         DbgPrint ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
502         DbgPrint ( "  DHCP = %s\n", ibft_ipaddr ( &nic->dhcp ) );
503         DbgPrint ( "  VLAN = %04x\n", nic->vlan );
504         DbgPrint ( "  MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
505                    nic->mac_address[0], nic->mac_address[1],
506                    nic->mac_address[2], nic->mac_address[3],
507                    nic->mac_address[4], nic->mac_address[5] );
508         DbgPrint ( "  PCI = %02x:%02x.%x\n",
509                    ( ( nic->pci_bus_dev_func >> 8 ) & 0xff ),
510                    ( ( nic->pci_bus_dev_func >> 3 ) & 0x1f ),
511                    ( ( nic->pci_bus_dev_func >> 0 ) & 0x07 ) );
512         DbgPrint ( "  Hostname = %s\n", ibft_string ( ibft, &nic->hostname ) );
513
514         /* Get list of all objects providing GUID_NDIS_LAN_CLASS interface */
515         status = IoGetDeviceInterfaces ( &GUID_NDIS_LAN_CLASS, NULL,
516                                          DEVICE_INTERFACE_INCLUDE_NONACTIVE,
517                                          &symlinks );
518         if ( ! NT_SUCCESS ( status ) ) {
519                 DbgPrint ( "Could not fetch NIC list: %x\n", status );
520                 return;
521         }
522
523         /* Configure any matching NICs */
524         for ( symlink = symlinks ;
525               RtlInitUnicodeString ( &u_symlink, symlink ) , *symlink ;
526               symlink += ( ( u_symlink.Length / sizeof ( *symlink ) ) + 1 ) ) {
527                 try_configure_nic ( nic, &u_symlink );
528         }
529
530         /* Free object list */
531         ExFreePool ( symlinks );
532 }
533
534 /**
535  * Parse iBFT target structure
536  *
537  * @v ibft              iBFT
538  * @v target            Target structure
539  */
540 static VOID parse_ibft_target ( PIBFT_TABLE ibft, PIBFT_TARGET target ) {
541         PIBFT_HEADER header = &target->header;
542
543         /* Dump structure information */
544         DbgPrint ( "Found iBFT target %d:\n", header->index );
545         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
546                    ( header->flags & IBFT_FL_TARGET_BLOCK_VALID
547                      ? ", valid" : "" ),
548                    ( header->flags & IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED
549                      ? ", boot selected" : "" ),
550                    ( header->flags & IBFT_FL_TARGET_USE_CHAP
551                      ? ", Radius CHAP" : "" ),
552                    ( header->flags & IBFT_FL_TARGET_USE_RCHAP
553                      ? ", Radius rCHAP" : "" ) );
554         if ( ! ( header->flags & IBFT_FL_TARGET_BLOCK_VALID ) )
555                 return;
556         DbgPrint ( "  IP = %s\n",
557                    ibft_ipaddr ( &target->ip_address ) );
558         DbgPrint ( "  Port = %d\n", target->socket );
559         DbgPrint ( "  LUN = %04x-%04x-%04x-%04x\n",
560                    ( ( target->boot_lun >> 48 ) & 0xffff ),
561                    ( ( target->boot_lun >> 32 ) & 0xffff ),
562                    ( ( target->boot_lun >> 16 ) & 0xffff ),
563                    ( ( target->boot_lun >> 0  ) & 0xffff ) );
564         DbgPrint ( "  CHAP type = %d (%s)\n", target->chap_type,
565                    ( ( target->chap_type == IBFT_CHAP_NONE ) ? "None" :
566                      ( ( target->chap_type == IBFT_CHAP_ONE_WAY ) ? "One-way" :
567                        ( ( target->chap_type == IBFT_CHAP_MUTUAL ) ? "Mutual" :
568                          "Unknown" ) ) ) );
569         DbgPrint ( "  NIC = %d\n", target->nic_association );
570         DbgPrint ( "  Name = %s\n",
571                    ibft_string ( ibft, &target->target_name ) );
572         DbgPrint ( "  CHAP name = %s\n",
573                    ibft_string ( ibft, &target->chap_name ) );
574         DbgPrint ( "  CHAP secret = %s\n",
575                    ( ibft_string_exists ( &target->chap_secret ) ?
576                      "<omitted>" : "" ) );
577         DbgPrint ( "  Reverse CHAP name = %s\n",
578                    ibft_string ( ibft, &target->reverse_chap_name ) );
579         DbgPrint ( "  Reverse CHAP secret = %s\n",
580                    ( ibft_string_exists ( &target->reverse_chap_secret ) ?
581                      "<omitted>" : "" ) );
582 }
583
584 /**
585  * Parse iBFT
586  *
587  * @v ibft              iBFT
588  */
589 VOID parse_ibft ( PIBFT_TABLE ibft ) {
590         PIBFT_CONTROL control = &ibft->control;
591         PUSHORT offset;
592         PIBFT_HEADER header;
593
594         /* Scan through all entries in the Control structure */
595         for ( offset = &control->extensions ;
596               ( ( PUCHAR ) offset ) <
597                       ( ( ( PUCHAR ) control ) + control->header.length ) ;
598               offset++ ) {
599                 if ( ! *offset )
600                         continue;
601                 header = ( ( PIBFT_HEADER ) ( ( ( PUCHAR ) ibft ) + *offset ));
602                 switch ( header->structure_id ) {
603                 case IBFT_STRUCTURE_ID_INITIATOR :
604                         parse_ibft_initiator ( ibft,
605                                                ( ( PIBFT_INITIATOR ) header ));
606                         break;
607                 case IBFT_STRUCTURE_ID_NIC :
608                         parse_ibft_nic ( ibft, ( ( PIBFT_NIC ) header ) );
609                         break;
610                 case IBFT_STRUCTURE_ID_TARGET :
611                         parse_ibft_target ( ibft,
612                                             ( ( PIBFT_TARGET ) header ) );
613                         break;
614                 default :
615                         DbgPrint ( "Ignoring unknown iBFT structure ID %d "
616                                    "index %d\n", header->structure_id,
617                                    header->index );
618                         break;
619                 }
620         }
621 }