|
|
1.1 ! root 1: /* ! 2: * Copyright Fen Systems Ltd. 2007. Portions of this code are derived ! 3: * from IBM Corporation Sample Programs. Copyright IBM Corporation ! 4: * 2004, 2007. All rights reserved. ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person ! 7: * obtaining a copy of this software and associated documentation ! 8: * files (the "Software"), to deal in the Software without ! 9: * restriction, including without limitation the rights to use, copy, ! 10: * modify, merge, publish, distribute, sublicense, and/or sell copies ! 11: * of the Software, and to permit persons to whom the Software is ! 12: * furnished to do so, subject to the following conditions: ! 13: * ! 14: * The above copyright notice and this permission notice shall be ! 15: * included in all copies or substantial portions of the Software. ! 16: * ! 17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ! 18: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ! 19: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ! 20: * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ! 21: * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ! 22: * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ! 23: * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ! 24: * SOFTWARE. ! 25: * ! 26: */ ! 27: ! 28: FILE_LICENCE ( BSD2 ); ! 29: ! 30: #include <stdint.h> ! 31: #include <stdio.h> ! 32: #include <string.h> ! 33: #include <errno.h> ! 34: #include <byteswap.h> ! 35: #include <ipxe/pci.h> ! 36: #include <ipxe/acpi.h> ! 37: #include <ipxe/in.h> ! 38: #include <ipxe/netdevice.h> ! 39: #include <ipxe/ethernet.h> ! 40: #include <ipxe/dhcp.h> ! 41: #include <ipxe/iscsi.h> ! 42: #include <ipxe/ibft.h> ! 43: ! 44: /** @file ! 45: * ! 46: * iSCSI boot firmware table ! 47: * ! 48: * The information in this file is derived from the document "iSCSI ! 49: * Boot Firmware Table (iBFT)" as published by IBM at ! 50: * ! 51: * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf ! 52: * ! 53: */ ! 54: ! 55: /** ! 56: * An iBFT created by iPXE ! 57: * ! 58: */ ! 59: struct ipxe_ibft { ! 60: /** The fixed section */ ! 61: struct ibft_table table; ! 62: /** The Initiator section */ ! 63: struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) )); ! 64: /** The NIC section */ ! 65: struct ibft_nic nic __attribute__ (( aligned ( 16 ) )); ! 66: /** The Target section */ ! 67: struct ibft_target target __attribute__ (( aligned ( 16 ) )); ! 68: /** Strings block */ ! 69: char strings[0]; ! 70: } __attribute__ (( packed, aligned ( 16 ) )); ! 71: ! 72: /** ! 73: * iSCSI string block descriptor ! 74: * ! 75: * This is an internal structure that we use to keep track of the ! 76: * allocation of string data. ! 77: */ ! 78: struct ibft_strings { ! 79: /** The iBFT containing these strings */ ! 80: struct ibft_table *table; ! 81: /** Offset of first free byte within iBFT */ ! 82: size_t offset; ! 83: /** Total length of the iBFT */ ! 84: size_t len; ! 85: }; ! 86: ! 87: /** ! 88: * Fill in an IP address field within iBFT ! 89: * ! 90: * @v ipaddr IP address field ! 91: * @v in IPv4 address ! 92: */ ! 93: static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) { ! 94: memset ( ipaddr, 0, sizeof ( *ipaddr ) ); ! 95: if ( in.s_addr ) { ! 96: ipaddr->in = in; ! 97: ipaddr->ones = 0xffff; ! 98: } ! 99: } ! 100: ! 101: /** ! 102: * Fill in an IP address within iBFT from configuration setting ! 103: * ! 104: * @v ipaddr IP address field ! 105: * @v setting Configuration setting ! 106: * @v count Maximum number of IP addresses ! 107: */ ! 108: static void ibft_set_ipaddr_setting ( struct ibft_ipaddr *ipaddr, ! 109: struct setting *setting, ! 110: unsigned int count ) { ! 111: struct in_addr in[count]; ! 112: unsigned int i; ! 113: ! 114: fetch_ipv4_array_setting ( NULL, setting, in, count ); ! 115: for ( i = 0 ; i < count ; i++ ) { ! 116: ibft_set_ipaddr ( &ipaddr[i], in[i] ); ! 117: } ! 118: } ! 119: ! 120: /** ! 121: * Read IP address from iBFT (for debugging) ! 122: * ! 123: * @v strings iBFT string block descriptor ! 124: * @v string String field ! 125: * @ret ipaddr IP address string ! 126: */ ! 127: static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) { ! 128: return inet_ntoa ( ipaddr->in ); ! 129: } ! 130: ! 131: /** ! 132: * Allocate a string within iBFT ! 133: * ! 134: * @v strings iBFT string block descriptor ! 135: * @v string String field to fill in ! 136: * @v len Length of string to allocate (excluding NUL) ! 137: * @ret dest String destination, or NULL ! 138: */ ! 139: static char * ibft_alloc_string ( struct ibft_strings *strings, ! 140: struct ibft_string *string, size_t len ) { ! 141: ! 142: if ( ( strings->offset + len ) >= strings->len ) ! 143: return NULL; ! 144: ! 145: string->offset = cpu_to_le16 ( strings->offset ); ! 146: string->len = cpu_to_le16 ( len ); ! 147: strings->offset += ( len + 1 ); ! 148: ! 149: return ( ( ( char * ) strings->table ) + string->offset ); ! 150: } ! 151: ! 152: /** ! 153: * Fill in a string field within iBFT ! 154: * ! 155: * @v strings iBFT string block descriptor ! 156: * @v string String field ! 157: * @v data String to fill in, or NULL ! 158: * @ret rc Return status code ! 159: */ ! 160: static int ibft_set_string ( struct ibft_strings *strings, ! 161: struct ibft_string *string, const char *data ) { ! 162: char *dest; ! 163: ! 164: if ( ! data ) ! 165: return 0; ! 166: ! 167: dest = ibft_alloc_string ( strings, string, strlen ( data ) ); ! 168: if ( ! dest ) ! 169: return -ENOBUFS; ! 170: strcpy ( dest, data ); ! 171: ! 172: return 0; ! 173: } ! 174: ! 175: /** ! 176: * Fill in a string field within iBFT from configuration setting ! 177: * ! 178: * @v strings iBFT string block descriptor ! 179: * @v string String field ! 180: * @v setting Configuration setting ! 181: * @ret rc Return status code ! 182: */ ! 183: static int ibft_set_string_setting ( struct ibft_strings *strings, ! 184: struct ibft_string *string, ! 185: struct setting *setting ) { ! 186: int len; ! 187: char *dest; ! 188: ! 189: len = fetch_setting_len ( NULL, setting ); ! 190: if ( len < 0 ) { ! 191: string->offset = 0; ! 192: string->len = 0; ! 193: return 0; ! 194: } ! 195: ! 196: dest = ibft_alloc_string ( strings, string, len ); ! 197: if ( ! dest ) ! 198: return -ENOBUFS; ! 199: fetch_string_setting ( NULL, setting, dest, ( len + 1 ) ); ! 200: ! 201: return 0; ! 202: } ! 203: ! 204: /** ! 205: * Read string from iBFT (for debugging) ! 206: * ! 207: * @v strings iBFT string block descriptor ! 208: * @v string String field ! 209: * @ret data String content (or "<empty>") ! 210: */ ! 211: static const char * ibft_string ( struct ibft_strings *strings, ! 212: struct ibft_string *string ) { ! 213: return ( string->offset ? ! 214: ( ( ( char * ) strings->table ) + string->offset ) : NULL ); ! 215: } ! 216: ! 217: /** ! 218: * Fill in NIC portion of iBFT ! 219: * ! 220: * @v nic NIC portion of iBFT ! 221: * @v strings iBFT string block descriptor ! 222: * @v netdev Network device ! 223: * @ret rc Return status code ! 224: */ ! 225: static int ibft_fill_nic ( struct ibft_nic *nic, ! 226: struct ibft_strings *strings, ! 227: struct net_device *netdev ) { ! 228: struct ll_protocol *ll_protocol = netdev->ll_protocol; ! 229: struct in_addr netmask_addr = { 0 }; ! 230: unsigned int netmask_count = 0; ! 231: int rc; ! 232: ! 233: /* Fill in common header */ ! 234: nic->header.structure_id = IBFT_STRUCTURE_ID_NIC; ! 235: nic->header.version = 1; ! 236: nic->header.length = cpu_to_le16 ( sizeof ( *nic ) ); ! 237: nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID | ! 238: IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ); ! 239: ! 240: /* Extract values from configuration settings */ ! 241: ibft_set_ipaddr_setting ( &nic->ip_address, &ip_setting, 1 ); ! 242: DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) ); ! 243: ibft_set_ipaddr_setting ( &nic->gateway, &gateway_setting, 1 ); ! 244: DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) ); ! 245: ibft_set_ipaddr_setting ( &nic->dns[0], &dns_setting, ! 246: ( sizeof ( nic->dns ) / ! 247: sizeof ( nic->dns[0] ) ) ); ! 248: DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) ); ! 249: DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) ); ! 250: if ( ( rc = ibft_set_string_setting ( strings, &nic->hostname, ! 251: &hostname_setting ) ) != 0 ) ! 252: return rc; ! 253: DBG ( "iBFT NIC hostname = %s\n", ! 254: ibft_string ( strings, &nic->hostname ) ); ! 255: ! 256: /* Derive subnet mask prefix from subnet mask */ ! 257: fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr ); ! 258: while ( netmask_addr.s_addr ) { ! 259: if ( netmask_addr.s_addr & 0x1 ) ! 260: netmask_count++; ! 261: netmask_addr.s_addr >>= 1; ! 262: } ! 263: nic->subnet_mask_prefix = netmask_count; ! 264: DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix ); ! 265: ! 266: /* Extract values from net-device configuration */ ! 267: if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr, ! 268: nic->mac_address ) ) != 0 ) { ! 269: DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) ); ! 270: return rc; ! 271: } ! 272: DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) ); ! 273: nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location ); ! 274: DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) ); ! 275: ! 276: return 0; ! 277: } ! 278: ! 279: /** ! 280: * Fill in Initiator portion of iBFT ! 281: * ! 282: * @v initiator Initiator portion of iBFT ! 283: * @v strings iBFT string block descriptor ! 284: * @v iscsi iSCSI session ! 285: * @ret rc Return status code ! 286: */ ! 287: static int ibft_fill_initiator ( struct ibft_initiator *initiator, ! 288: struct ibft_strings *strings, ! 289: struct iscsi_session *iscsi ) { ! 290: int rc; ! 291: ! 292: /* Fill in common header */ ! 293: initiator->header.structure_id = IBFT_STRUCTURE_ID_INITIATOR; ! 294: initiator->header.version = 1; ! 295: initiator->header.length = cpu_to_le16 ( sizeof ( *initiator ) ); ! 296: initiator->header.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID | ! 297: IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ); ! 298: ! 299: /* Fill in hostname */ ! 300: if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name, ! 301: iscsi->initiator_iqn ) ) != 0 ) ! 302: return rc; ! 303: DBG ( "iBFT initiator hostname = %s\n", ! 304: ibft_string ( strings, &initiator->initiator_name ) ); ! 305: ! 306: return 0; ! 307: } ! 308: ! 309: /** ! 310: * Fill in Target CHAP portion of iBFT ! 311: * ! 312: * @v target Target portion of iBFT ! 313: * @v strings iBFT string block descriptor ! 314: * @v iscsi iSCSI session ! 315: * @ret rc Return status code ! 316: */ ! 317: static int ibft_fill_target_chap ( struct ibft_target *target, ! 318: struct ibft_strings *strings, ! 319: struct iscsi_session *iscsi ) { ! 320: int rc; ! 321: ! 322: if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_FORWARD_REQUIRED ) ) ! 323: return 0; ! 324: ! 325: assert ( iscsi->initiator_username ); ! 326: assert ( iscsi->initiator_password ); ! 327: ! 328: target->chap_type = IBFT_CHAP_ONE_WAY; ! 329: if ( ( rc = ibft_set_string ( strings, &target->chap_name, ! 330: iscsi->initiator_username ) ) != 0 ) ! 331: return rc; ! 332: DBG ( "iBFT target username = %s\n", ! 333: ibft_string ( strings, &target->chap_name ) ); ! 334: if ( ( rc = ibft_set_string ( strings, &target->chap_secret, ! 335: iscsi->initiator_password ) ) != 0 ) ! 336: return rc; ! 337: DBG ( "iBFT target password = <redacted>\n" ); ! 338: ! 339: return 0; ! 340: } ! 341: ! 342: /** ! 343: * Fill in Target Reverse CHAP portion of iBFT ! 344: * ! 345: * @v target Target portion of iBFT ! 346: * @v strings iBFT string block descriptor ! 347: * @v iscsi iSCSI session ! 348: * @ret rc Return status code ! 349: */ ! 350: static int ibft_fill_target_reverse_chap ( struct ibft_target *target, ! 351: struct ibft_strings *strings, ! 352: struct iscsi_session *iscsi ) { ! 353: int rc; ! 354: ! 355: if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) ) ! 356: return 0; ! 357: ! 358: assert ( iscsi->initiator_username ); ! 359: assert ( iscsi->initiator_password ); ! 360: assert ( iscsi->target_username ); ! 361: assert ( iscsi->target_password ); ! 362: ! 363: target->chap_type = IBFT_CHAP_MUTUAL; ! 364: if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name, ! 365: iscsi->target_username ) ) != 0 ) ! 366: return rc; ! 367: DBG ( "iBFT target reverse username = %s\n", ! 368: ibft_string ( strings, &target->chap_name ) ); ! 369: if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret, ! 370: iscsi->target_password ) ) != 0 ) ! 371: return rc; ! 372: DBG ( "iBFT target reverse password = <redacted>\n" ); ! 373: ! 374: return 0; ! 375: } ! 376: ! 377: /** ! 378: * Fill in Target portion of iBFT ! 379: * ! 380: * @v target Target portion of iBFT ! 381: * @v strings iBFT string block descriptor ! 382: * @v iscsi iSCSI session ! 383: * @ret rc Return status code ! 384: */ ! 385: static int ibft_fill_target ( struct ibft_target *target, ! 386: struct ibft_strings *strings, ! 387: struct iscsi_session *iscsi ) { ! 388: struct sockaddr_in *sin_target = ! 389: ( struct sockaddr_in * ) &iscsi->target_sockaddr; ! 390: int rc; ! 391: ! 392: /* Fill in common header */ ! 393: target->header.structure_id = IBFT_STRUCTURE_ID_TARGET; ! 394: target->header.version = 1; ! 395: target->header.length = cpu_to_le16 ( sizeof ( *target ) ); ! 396: target->header.flags = ( IBFT_FL_TARGET_BLOCK_VALID | ! 397: IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ); ! 398: ! 399: /* Fill in Target values */ ! 400: ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr ); ! 401: DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) ); ! 402: target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) ); ! 403: DBG ( "iBFT target port = %d\n", target->socket ); ! 404: memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) ); ! 405: DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n", ! 406: SCSI_LUN_DATA ( target->boot_lun ) ); ! 407: if ( ( rc = ibft_set_string ( strings, &target->target_name, ! 408: iscsi->target_iqn ) ) != 0 ) ! 409: return rc; ! 410: DBG ( "iBFT target name = %s\n", ! 411: ibft_string ( strings, &target->target_name ) ); ! 412: if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 ) ! 413: return rc; ! 414: if ( ( rc = ibft_fill_target_reverse_chap ( target, strings, ! 415: iscsi ) ) != 0 ) ! 416: return rc; ! 417: ! 418: return 0; ! 419: } ! 420: ! 421: /** ! 422: * Fill in iBFT ! 423: * ! 424: * @v iscsi iSCSI session ! 425: * @v acpi ACPI table ! 426: * @v len Length of ACPI table ! 427: * @ret rc Return status code ! 428: */ ! 429: int ibft_describe ( struct iscsi_session *iscsi, ! 430: struct acpi_description_header *acpi, ! 431: size_t len ) { ! 432: struct ipxe_ibft *ibft = ! 433: container_of ( acpi, struct ipxe_ibft, table.acpi ); ! 434: struct ibft_strings strings = { ! 435: .table = &ibft->table, ! 436: .offset = offsetof ( typeof ( *ibft ), strings ), ! 437: .len = len, ! 438: }; ! 439: struct net_device *netdev; ! 440: int rc; ! 441: ! 442: /* Ugly hack. Now that we have a generic interface mechanism ! 443: * that can support ioctls, we can potentially eliminate this. ! 444: */ ! 445: netdev = last_opened_netdev(); ! 446: if ( ! netdev ) { ! 447: DBGC ( iscsi, "iSCSI %p cannot guess network device\n", ! 448: iscsi ); ! 449: return -ENODEV; ! 450: } ! 451: ! 452: /* Fill in ACPI header */ ! 453: ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG ); ! 454: ibft->table.acpi.length = cpu_to_le32 ( len ); ! 455: ibft->table.acpi.revision = 1; ! 456: ! 457: /* Fill in Control block */ ! 458: ibft->table.control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL; ! 459: ibft->table.control.header.version = 1; ! 460: ibft->table.control.header.length = ! 461: cpu_to_le16 ( sizeof ( ibft->table.control ) ); ! 462: ibft->table.control.initiator = ! 463: cpu_to_le16 ( offsetof ( typeof ( *ibft ), initiator ) ); ! 464: ibft->table.control.nic_0 = ! 465: cpu_to_le16 ( offsetof ( typeof ( *ibft ), nic ) ); ! 466: ibft->table.control.target_0 = ! 467: cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) ); ! 468: ! 469: /* Fill in NIC, Initiator and Target blocks */ ! 470: if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 ) ! 471: return rc; ! 472: if ( ( rc = ibft_fill_initiator ( &ibft->initiator, &strings, ! 473: iscsi ) ) != 0 ) ! 474: return rc; ! 475: if ( ( rc = ibft_fill_target ( &ibft->target, &strings, ! 476: iscsi ) ) != 0 ) ! 477: return rc; ! 478: ! 479: return 0; ! 480: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.