ibft.c

Go to the documentation of this file.
00001 /*
00002  * Copyright Fen Systems Ltd. 2007.  Portions of this code are derived
00003  * from IBM Corporation Sample Programs.  Copyright IBM Corporation
00004  * 2004, 2007.  All rights reserved.
00005  *
00006  * Permission is hereby granted, free of charge, to any person
00007  * obtaining a copy of this software and associated documentation
00008  * files (the "Software"), to deal in the Software without
00009  * restriction, including without limitation the rights to use, copy,
00010  * modify, merge, publish, distribute, sublicense, and/or sell copies
00011  * of the Software, and to permit persons to whom the Software is
00012  * furnished to do so, subject to the following conditions:
00013  *
00014  * The above copyright notice and this permission notice shall be
00015  * included in all copies or substantial portions of the Software.
00016  *
00017  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00018  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00019  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00020  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00021  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00022  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00023  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00024  * SOFTWARE.
00025  *
00026  */
00027 
00028 FILE_LICENCE ( BSD2 );
00029 
00030 #include <stdint.h>
00031 #include <stdio.h>
00032 #include <string.h>
00033 #include <errno.h>
00034 #include <byteswap.h>
00035 #include <realmode.h>
00036 #include <gpxe/pci.h>
00037 #include <gpxe/acpi.h>
00038 #include <gpxe/in.h>
00039 #include <gpxe/netdevice.h>
00040 #include <gpxe/ethernet.h>
00041 #include <gpxe/dhcp.h>
00042 #include <gpxe/iscsi.h>
00043 #include <gpxe/ibft.h>
00044 
00045 /** @file
00046  *
00047  * iSCSI boot firmware table
00048  *
00049  * The information in this file is derived from the document "iSCSI
00050  * Boot Firmware Table (iBFT)" as published by IBM at
00051  *
00052  * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf
00053  *
00054  */
00055 
00056 #define ibftab __use_data16 ( ibftab )
00057 /** The iBFT used by gPXE */
00058 struct gpxe_ibft __data16 ( ibftab ) = {
00059         /* Table header */
00060         .table = {
00061                 /* ACPI header */
00062                 .acpi = {
00063                         .signature = IBFT_SIG,
00064                         .length = sizeof ( ibftab ),
00065                         .revision = 1,
00066                         .oem_id = "FENSYS",
00067                         .oem_table_id = "gPXE",
00068                 },
00069                 /* Control block */
00070                 .control = {
00071                         .header = {
00072                                 .structure_id = IBFT_STRUCTURE_ID_CONTROL,
00073                                 .version = 1,
00074                                 .length = sizeof ( ibftab.table.control ),
00075                                 .flags = 0,
00076                         },
00077                         .initiator = offsetof ( typeof ( ibftab ), initiator ),
00078                         .nic_0 = offsetof ( typeof ( ibftab ), nic ),
00079                         .target_0 = offsetof ( typeof ( ibftab ), target ),
00080                 },
00081         },
00082         /* iSCSI initiator information */
00083         .initiator = {
00084                 .header = {
00085                         .structure_id = IBFT_STRUCTURE_ID_INITIATOR,
00086                         .version = 1,
00087                         .length = sizeof ( ibftab.initiator ),
00088                         .flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
00089                                    IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ),
00090                 },
00091         },
00092         /* NIC information */
00093         .nic = {
00094                 .header = {
00095                         .structure_id = IBFT_STRUCTURE_ID_NIC,
00096                         .version = 1,
00097                         .length = sizeof ( ibftab.nic ),
00098                         .flags = ( IBFT_FL_NIC_BLOCK_VALID |
00099                                    IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ),
00100                 },
00101         },
00102         /* iSCSI target information */
00103         .target = {
00104                 .header = {
00105                         .structure_id = IBFT_STRUCTURE_ID_TARGET,
00106                         .version = 1,
00107                         .length = sizeof ( ibftab.target ),
00108                         .flags = ( IBFT_FL_TARGET_BLOCK_VALID |
00109                                    IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ),
00110                 },
00111         },
00112 };
00113 
00114 /**
00115  * Fill in an IP address field within iBFT
00116  *
00117  * @v ipaddr            IP address field
00118  * @v in                IPv4 address
00119  */
00120 static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) {
00121         memset ( ipaddr, 0, sizeof ( ipaddr ) );
00122         if ( in.s_addr ) {
00123                 ipaddr->in = in;
00124                 ipaddr->ones = 0xffff;
00125         }
00126 }
00127 
00128 /**
00129  * Fill in an IP address within iBFT from configuration setting
00130  *
00131  * @v ipaddr            IP address field
00132  * @v setting           Configuration setting
00133  * @v tag               DHCP option tag
00134  */
00135 static void ibft_set_ipaddr_option ( struct ibft_ipaddr *ipaddr,
00136                                      struct setting *setting ) {
00137         struct in_addr in = { 0 };
00138         fetch_ipv4_setting ( NULL, setting, &in );
00139         ibft_set_ipaddr ( ipaddr, in );
00140 }
00141 
00142 /**
00143  * Read IP address from iBFT (for debugging)
00144  *
00145  * @v strings           iBFT string block descriptor
00146  * @v string            String field
00147  * @ret ipaddr          IP address string
00148  */
00149 static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) {
00150         return inet_ntoa ( ipaddr->in );
00151 }
00152 
00153 /**
00154  * Allocate a string within iBFT
00155  *
00156  * @v strings           iBFT string block descriptor
00157  * @v string            String field to fill in
00158  * @v len               Length of string to allocate (excluding NUL)
00159  * @ret rc              Return status code
00160  */
00161 static int ibft_alloc_string ( struct ibft_string_block *strings,
00162                                struct ibft_string *string, size_t len ) {
00163         char *dest;
00164         unsigned int remaining;
00165 
00166         dest = ( ( ( char * ) strings->table ) + strings->offset );
00167         remaining = ( strings->table->acpi.length - strings->offset );
00168         if ( len >= remaining )
00169                 return -ENOMEM;
00170 
00171         string->offset = strings->offset;
00172         string->length = len;
00173         strings->offset += ( len + 1 );
00174         return 0;
00175 }
00176 
00177 /**
00178  * Fill in a string field within iBFT
00179  *
00180  * @v strings           iBFT string block descriptor
00181  * @v string            String field
00182  * @v data              String to fill in, or NULL
00183  * @ret rc              Return status code
00184  */
00185 static int ibft_set_string ( struct ibft_string_block *strings,
00186                              struct ibft_string *string, const char *data ) {
00187         char *dest;
00188         int rc;
00189 
00190         if ( ! data )
00191                 return 0;
00192 
00193         if ( ( rc = ibft_alloc_string ( strings, string,
00194                                         strlen ( data ) ) ) != 0 )
00195                 return rc;
00196         dest = ( ( ( char * ) strings->table ) + string->offset );
00197         strcpy ( dest, data );
00198 
00199         return 0;
00200 }
00201 
00202 /**
00203  * Fill in a string field within iBFT from configuration setting
00204  *
00205  * @v strings           iBFT string block descriptor
00206  * @v string            String field
00207  * @v setting           Configuration setting
00208  * @ret rc              Return status code
00209  */
00210 static int ibft_set_string_option ( struct ibft_string_block *strings,
00211                                     struct ibft_string *string,
00212                                     struct setting *setting ) {
00213         int len;
00214         char *dest;
00215         int rc;
00216 
00217         len = fetch_setting_len ( NULL, setting );
00218         if ( len < 0 ) {
00219                 string->offset = 0;
00220                 string->length = 0;
00221                 return 0;
00222         }
00223 
00224         if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
00225                 return rc;
00226         dest = ( ( ( char * ) strings->table ) + string->offset );
00227         fetch_string_setting ( NULL, setting, dest, ( len + 1 ) );
00228         return 0;
00229 }
00230 
00231 /**
00232  * Read string from iBFT (for debugging)
00233  *
00234  * @v strings           iBFT string block descriptor
00235  * @v string            String field
00236  * @ret data            String content (or "<empty>")
00237  */
00238 static const char * ibft_string ( struct ibft_string_block *strings,
00239                                   struct ibft_string *string ) {
00240         return ( string->offset ?
00241                  ( ( ( char * ) strings->table ) + string->offset ) : NULL );
00242 }
00243 
00244 /**
00245  * Fill in NIC portion of iBFT
00246  *
00247  * @v nic               NIC portion of iBFT
00248  * @v strings           iBFT string block descriptor
00249  * @v netdev            Network device
00250  * @ret rc              Return status code
00251  */
00252 static int ibft_fill_nic ( struct ibft_nic *nic,
00253                            struct ibft_string_block *strings,
00254                            struct net_device *netdev ) {
00255         struct ll_protocol *ll_protocol = netdev->ll_protocol;
00256         struct in_addr netmask_addr = { 0 };
00257         unsigned int netmask_count = 0;
00258         int rc;
00259 
00260         /* Extract values from DHCP configuration */
00261         ibft_set_ipaddr_option ( &nic->ip_address, &ip_setting );
00262         DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
00263         ibft_set_ipaddr_option ( &nic->gateway, &gateway_setting );
00264         DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
00265         ibft_set_ipaddr_option ( &nic->dns[0], &dns_setting );
00266         DBG ( "iBFT NIC DNS = %s\n", ibft_ipaddr ( &nic->dns[0] ) );
00267         if ( ( rc = ibft_set_string_option ( strings, &nic->hostname,
00268                                              &hostname_setting ) ) != 0 )
00269                 return rc;
00270         DBG ( "iBFT NIC hostname = %s\n",
00271               ibft_string ( strings, &nic->hostname ) );
00272 
00273         /* Derive subnet mask prefix from subnet mask */
00274         fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr );
00275         while ( netmask_addr.s_addr ) {
00276                 if ( netmask_addr.s_addr & 0x1 )
00277                         netmask_count++;
00278                 netmask_addr.s_addr >>= 1;
00279         }
00280         nic->subnet_mask_prefix = netmask_count;
00281         DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
00282 
00283         /* Extract values from net-device configuration */
00284         if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr,
00285                                             nic->mac_address ) ) != 0 ) {
00286                 DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) );
00287                 return rc;
00288         }
00289         DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) );
00290         nic->pci_bus_dev_func = netdev->dev->desc.location;
00291         DBG ( "iBFT NIC PCI = %04x\n", nic->pci_bus_dev_func );
00292 
00293         return 0;
00294 }
00295 
00296 /**
00297  * Fill in Initiator portion of iBFT
00298  *
00299  * @v initiator         Initiator portion of iBFT
00300  * @v strings           iBFT string block descriptor
00301  * @ret rc              Return status code
00302  */
00303 static int ibft_fill_initiator ( struct ibft_initiator *initiator,
00304                                  struct ibft_string_block *strings ) {
00305         const char *initiator_iqn = iscsi_initiator_iqn();
00306         int rc;
00307 
00308         if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name,
00309                                       initiator_iqn ) ) != 0 )
00310                 return rc;
00311         DBG ( "iBFT initiator hostname = %s\n",
00312               ibft_string ( strings, &initiator->initiator_name ) );
00313 
00314         return 0;
00315 }
00316 
00317 /**
00318  * Fill in Target CHAP portion of iBFT
00319  *
00320  * @v target            Target portion of iBFT
00321  * @v strings           iBFT string block descriptor
00322  * @v iscsi             iSCSI session
00323  * @ret rc              Return status code
00324  */
00325 static int ibft_fill_target_chap ( struct ibft_target *target,
00326                                    struct ibft_string_block *strings,
00327                                    struct iscsi_session *iscsi ) {
00328         int rc;
00329 
00330         if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_FORWARD_REQUIRED ) )
00331                 return 0;
00332 
00333         assert ( iscsi->initiator_username );
00334         assert ( iscsi->initiator_password );
00335 
00336         target->chap_type = IBFT_CHAP_ONE_WAY;
00337         if ( ( rc = ibft_set_string ( strings, &target->chap_name,
00338                                       iscsi->initiator_username ) ) != 0 )
00339                 return rc;
00340         DBG ( "iBFT target username = %s\n",
00341               ibft_string ( strings, &target->chap_name ) );
00342         if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
00343                                       iscsi->initiator_password ) ) != 0 )
00344                 return rc;
00345         DBG ( "iBFT target password = <redacted>\n" );
00346 
00347         return 0;
00348 }
00349 
00350 /**
00351  * Fill in Target Reverse CHAP portion of iBFT
00352  *
00353  * @v target            Target portion of iBFT
00354  * @v strings           iBFT string block descriptor
00355  * @v iscsi             iSCSI session
00356  * @ret rc              Return status code
00357  */
00358 static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
00359                                            struct ibft_string_block *strings,
00360                                            struct iscsi_session *iscsi ) {
00361         int rc;
00362 
00363         if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) )
00364                 return 0;
00365 
00366         assert ( iscsi->initiator_username );
00367         assert ( iscsi->initiator_password );
00368         assert ( iscsi->target_username );
00369         assert ( iscsi->target_password );
00370 
00371         target->chap_type = IBFT_CHAP_MUTUAL;
00372         if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name,
00373                                       iscsi->target_username ) ) != 0 )
00374                 return rc;
00375         DBG ( "iBFT target reverse username = %s\n",
00376               ibft_string ( strings, &target->chap_name ) );
00377         if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret,
00378                                       iscsi->target_password ) ) != 0 )
00379                 return rc;
00380         DBG ( "iBFT target reverse password = <redacted>\n" );
00381 
00382         return 0;
00383 }
00384 
00385 /**
00386  * Fill in Target portion of iBFT
00387  *
00388  * @v target            Target portion of iBFT
00389  * @v strings           iBFT string block descriptor
00390  * @v iscsi             iSCSI session
00391  * @ret rc              Return status code
00392  */
00393 static int ibft_fill_target ( struct ibft_target *target,
00394                               struct ibft_string_block *strings,
00395                               struct iscsi_session *iscsi ) {
00396         struct sockaddr_in *sin_target =
00397                 ( struct sockaddr_in * ) &iscsi->target_sockaddr;
00398         int rc;
00399 
00400         /* Fill in Target values */
00401         ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
00402         DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) );
00403         target->socket = ntohs ( sin_target->sin_port );
00404         DBG ( "iBFT target port = %d\n", target->socket );
00405         if ( ( rc = ibft_set_string ( strings, &target->target_name,
00406                                       iscsi->target_iqn ) ) != 0 )
00407                 return rc;
00408         DBG ( "iBFT target name = %s\n",
00409               ibft_string ( strings, &target->target_name ) );
00410         if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 )
00411                 return rc;
00412         if ( ( rc = ibft_fill_target_reverse_chap ( target, strings,
00413                                                     iscsi ) ) != 0 )
00414                 return rc;
00415 
00416         return 0;
00417 }
00418 
00419 /**
00420  * Fill in all variable portions of iBFT
00421  *
00422  * @v netdev            Network device
00423  * @v initiator_iqn     Initiator IQN
00424  * @v st_target         Target socket address
00425  * @v target_iqn        Target IQN
00426  * @ret rc              Return status code
00427  *
00428  */
00429 int ibft_fill_data ( struct net_device *netdev,
00430                      struct iscsi_session *iscsi ) {
00431         struct ibft_string_block strings = {
00432                 .table = &ibftab.table,
00433                 .offset = offsetof ( typeof ( ibftab ), strings ),
00434         };
00435         int rc;
00436 
00437         /* Fill in NIC, Initiator and Target portions */
00438         if ( ( rc = ibft_fill_nic ( &ibftab.nic, &strings, netdev ) ) != 0 )
00439                 return rc;
00440         if ( ( rc = ibft_fill_initiator ( &ibftab.initiator,
00441                                           &strings ) ) != 0 )
00442                 return rc;
00443         if ( ( rc = ibft_fill_target ( &ibftab.target, &strings,
00444                                        iscsi ) ) != 0 )
00445                 return rc;
00446 
00447         /* Update checksum */
00448         acpi_fix_checksum ( &ibftab.table.acpi );
00449 
00450         return 0;
00451 }

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1