Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Firmware: add iSCSI iBFT Support

Add /sysfs/firmware/ibft/[initiator|targetX|ethernetX] directories along with
text properties which export the the iSCSI Boot Firmware Table (iBFT)
structure.

What is iSCSI Boot Firmware Table? It is a mechanism for the iSCSI tools to
extract from the machine NICs the iSCSI connection information so that they
can automagically mount the iSCSI share/target. Currently the iSCSI
information is hard-coded in the initrd. The /sysfs entries are read-only
one-name-and-value fields.

The usual set of data exposed is:

# for a in `find /sys/firmware/ibft/ -type f -print`; do echo -n "$a: "; cat $a; done
/sys/firmware/ibft/target0/target-name: iqn.2007.com.intel-sbx44:storage-10gb
/sys/firmware/ibft/target0/nic-assoc: 0
/sys/firmware/ibft/target0/chap-type: 0
/sys/firmware/ibft/target0/lun: 00000000
/sys/firmware/ibft/target0/port: 3260
/sys/firmware/ibft/target0/ip-addr: 192.168.79.116
/sys/firmware/ibft/target0/flags: 3
/sys/firmware/ibft/target0/index: 0
/sys/firmware/ibft/ethernet0/mac: 00:11:25:9d:8b:01
/sys/firmware/ibft/ethernet0/vlan: 0
/sys/firmware/ibft/ethernet0/gateway: 192.168.79.254
/sys/firmware/ibft/ethernet0/origin: 0
/sys/firmware/ibft/ethernet0/subnet-mask: 255.255.252.0
/sys/firmware/ibft/ethernet0/ip-addr: 192.168.77.41
/sys/firmware/ibft/ethernet0/flags: 7
/sys/firmware/ibft/ethernet0/index: 0
/sys/firmware/ibft/initiator/initiator-name: iqn.2007-07.com:konrad.initiator
/sys/firmware/ibft/initiator/flags: 3
/sys/firmware/ibft/initiator/index: 0

For full details of the IBFT structure please take a look at:
ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf

[akpm@linux-foundation.org: fix build]
Signed-off-by: Konrad Rzeszutek <konradr@linux.vnet.ibm.com>
Cc: Mike Christie <michaelc@cs.wisc.edu>
Cc: Peter Jones <pjones@redhat.com>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


authored by

Konrad Rzeszutek and committed by
Greg Kroah-Hartman
138fe4e0 95bc6a10

+1168
+23
Documentation/ABI/testing/sysfs-ibft
··· 1 + What: /sys/firmware/ibft/initiator 2 + Date: November 2007 3 + Contact: Konrad Rzeszutek <ketuzsezr@darnok.org> 4 + Description: The /sys/firmware/ibft/initiator directory will contain 5 + files that expose the iSCSI Boot Firmware Table initiator data. 6 + Usually this contains the Initiator name. 7 + 8 + What: /sys/firmware/ibft/targetX 9 + Date: November 2007 10 + Contact: Konrad Rzeszutek <ketuzsezr@darnok.org> 11 + Description: The /sys/firmware/ibft/targetX directory will contain 12 + files that expose the iSCSI Boot Firmware Table target data. 13 + Usually this contains the target's IP address, boot LUN, 14 + target name, and what NIC it is associated with. It can also 15 + contain the CHAP name (and password), the reverse CHAP 16 + name (and password) 17 + 18 + What: /sys/firmware/ibft/ethernetX 19 + Date: November 2007 20 + Contact: Konrad Rzeszutek <ketuzsezr@darnok.org> 21 + Description: The /sys/firmware/ibft/ethernetX directory will contain 22 + files that expose the iSCSI Boot Firmware Table NIC data. 23 + This can this can the IP address, MAC, and gateway of the NIC.
+3
arch/x86/kernel/setup_32.c
··· 39 39 #include <linux/efi.h> 40 40 #include <linux/init.h> 41 41 #include <linux/edd.h> 42 + #include <linux/iscsi_ibft.h> 42 43 #include <linux/nodemask.h> 43 44 #include <linux/kexec.h> 44 45 #include <linux/crash_dump.h> ··· 690 689 #endif 691 690 numa_kva_reserve(); 692 691 reserve_crashkernel(); 692 + 693 + reserve_ibft_region(); 693 694 } 694 695 695 696 /*
+4
arch/x86/kernel/setup_64.c
··· 33 33 #include <linux/acpi.h> 34 34 #include <linux/kallsyms.h> 35 35 #include <linux/edd.h> 36 + #include <linux/iscsi_ibft.h> 36 37 #include <linux/mmzone.h> 37 38 #include <linux/kexec.h> 38 39 #include <linux/cpufreq.h> ··· 435 434 } 436 435 #endif 437 436 reserve_crashkernel(); 437 + 438 + reserve_ibft_region(); 439 + 438 440 paging_init(); 439 441 map_vsyscall(); 440 442
+20
drivers/firmware/Kconfig
··· 93 93 information from userspace through /sys/class/dmi/id/ or if you want 94 94 DMI-based module auto-loading. 95 95 96 + config ISCSI_IBFT_FIND 97 + bool "iSCSI Boot Firmware Table Attributes" 98 + depends on X86 99 + default n 100 + help 101 + This option enables the kernel to find the region of memory 102 + in which the ISCSI Boot Firmware Table (iBFT) resides. This 103 + is necessary for iSCSI Boot Firmware Table Attributes module to work 104 + properly. 105 + 106 + config ISCSI_IBFT 107 + tristate "iSCSI Boot Firmware Table Attributes module" 108 + depends on ISCSI_IBFT_FIND 109 + default n 110 + help 111 + This option enables support for detection and exposing of iSCSI 112 + Boot Firmware Table (iBFT) via sysfs to userspace. If you wish to 113 + detect iSCSI boot parameters dynamically during system boot, say Y. 114 + Otherwise, say N. 115 + 96 116 endmenu
+2
drivers/firmware/Makefile
··· 8 8 obj-$(CONFIG_DELL_RBU) += dell_rbu.o 9 9 obj-$(CONFIG_DCDBAS) += dcdbas.o 10 10 obj-$(CONFIG_DMIID) += dmi-id.o 11 + obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o 12 + obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
+982
drivers/firmware/iscsi_ibft.c
··· 1 + /* 2 + * Copyright 2007 Red Hat, Inc. 3 + * by Peter Jones <pjones@redhat.com> 4 + * Copyright 2008 IBM, Inc. 5 + * by Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 6 + * Copyright 2008 7 + * by Konrad Rzeszutek <ketuzsezr@darnok.org> 8 + * 9 + * This code exposes the iSCSI Boot Format Table to userland via sysfs. 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of the GNU General Public License v2.0 as published by 13 + * the Free Software Foundation 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + * 20 + * Changelog: 21 + * 22 + * 14 Mar 2008 - Konrad Rzeszutek <ketuzsezr@darnok.org> 23 + * Updated comments and copyrights. (v0.4.9) 24 + * 25 + * 11 Feb 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 26 + * Converted to using ibft_addr. (v0.4.8) 27 + * 28 + * 8 Feb 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 29 + * Combined two functions in one: reserve_ibft_region. (v0.4.7) 30 + * 31 + * 30 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 32 + * Added logic to handle IPv6 addresses. (v0.4.6) 33 + * 34 + * 25 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 35 + * Added logic to handle badly not-to-spec iBFT. (v0.4.5) 36 + * 37 + * 4 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 38 + * Added __init to function declarations. (v0.4.4) 39 + * 40 + * 21 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 41 + * Updated kobject registration, combined unregister functions in one 42 + * and code and style cleanup. (v0.4.3) 43 + * 44 + * 5 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 45 + * Added end-markers to enums and re-organized kobject registration. (v0.4.2) 46 + * 47 + * 4 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 48 + * Created 'device' sysfs link to the NIC and style cleanup. (v0.4.1) 49 + * 50 + * 28 Nov 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 51 + * Added sysfs-ibft documentation, moved 'find_ibft' function to 52 + * in its own file and added text attributes for every struct field. (v0.4) 53 + * 54 + * 21 Nov 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 55 + * Added text attributes emulating OpenFirmware /proc/device-tree naming. 56 + * Removed binary /sysfs interface (v0.3) 57 + * 58 + * 29 Aug 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 59 + * Added functionality in setup.c to reserve iBFT region. (v0.2) 60 + * 61 + * 27 Aug 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 62 + * First version exposing iBFT data via a binary /sysfs. (v0.1) 63 + * 64 + */ 65 + 66 + 67 + #include <linux/blkdev.h> 68 + #include <linux/capability.h> 69 + #include <linux/ctype.h> 70 + #include <linux/device.h> 71 + #include <linux/err.h> 72 + #include <linux/init.h> 73 + #include <linux/iscsi_ibft.h> 74 + #include <linux/limits.h> 75 + #include <linux/module.h> 76 + #include <linux/pci.h> 77 + #include <linux/slab.h> 78 + #include <linux/stat.h> 79 + #include <linux/string.h> 80 + #include <linux/types.h> 81 + 82 + #define IBFT_ISCSI_VERSION "0.4.9" 83 + #define IBFT_ISCSI_DATE "2008-Mar-14" 84 + 85 + MODULE_AUTHOR("Peter Jones <pjones@redhat.com> and \ 86 + Konrad Rzeszutek <ketuzsezr@darnok.org>"); 87 + MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information"); 88 + MODULE_LICENSE("GPL"); 89 + MODULE_VERSION(IBFT_ISCSI_VERSION); 90 + 91 + struct ibft_hdr { 92 + u8 id; 93 + u8 version; 94 + u16 length; 95 + u8 index; 96 + u8 flags; 97 + } __attribute__((__packed__)); 98 + 99 + struct ibft_control { 100 + struct ibft_hdr hdr; 101 + u16 extensions; 102 + u16 initiator_off; 103 + u16 nic0_off; 104 + u16 tgt0_off; 105 + u16 nic1_off; 106 + u16 tgt1_off; 107 + } __attribute__((__packed__)); 108 + 109 + struct ibft_initiator { 110 + struct ibft_hdr hdr; 111 + char isns_server[16]; 112 + char slp_server[16]; 113 + char pri_radius_server[16]; 114 + char sec_radius_server[16]; 115 + u16 initiator_name_len; 116 + u16 initiator_name_off; 117 + } __attribute__((__packed__)); 118 + 119 + struct ibft_nic { 120 + struct ibft_hdr hdr; 121 + char ip_addr[16]; 122 + u8 subnet_mask_prefix; 123 + u8 origin; 124 + char gateway[16]; 125 + char primary_dns[16]; 126 + char secondary_dns[16]; 127 + char dhcp[16]; 128 + u16 vlan; 129 + char mac[6]; 130 + u16 pci_bdf; 131 + u16 hostname_len; 132 + u16 hostname_off; 133 + } __attribute__((__packed__)); 134 + 135 + struct ibft_tgt { 136 + struct ibft_hdr hdr; 137 + char ip_addr[16]; 138 + u16 port; 139 + char lun[8]; 140 + u8 chap_type; 141 + u8 nic_assoc; 142 + u16 tgt_name_len; 143 + u16 tgt_name_off; 144 + u16 chap_name_len; 145 + u16 chap_name_off; 146 + u16 chap_secret_len; 147 + u16 chap_secret_off; 148 + u16 rev_chap_name_len; 149 + u16 rev_chap_name_off; 150 + u16 rev_chap_secret_len; 151 + u16 rev_chap_secret_off; 152 + } __attribute__((__packed__)); 153 + 154 + /* 155 + * The kobject different types and its names. 156 + * 157 + */ 158 + enum ibft_id { 159 + id_reserved = 0, /* We don't support. */ 160 + id_control = 1, /* Should show up only once and is not exported. */ 161 + id_initiator = 2, 162 + id_nic = 3, 163 + id_target = 4, 164 + id_extensions = 5, /* We don't support. */ 165 + id_end_marker, 166 + }; 167 + 168 + /* 169 + * We do not support the other types, hence the usage of NULL. 170 + * This maps to the enum ibft_id. 171 + */ 172 + static const char *ibft_id_names[] = 173 + {NULL, NULL, "initiator", "ethernet%d", "target%d", NULL, NULL}; 174 + 175 + /* 176 + * The text attributes names for each of the kobjects. 177 + */ 178 + enum ibft_eth_properties_enum { 179 + ibft_eth_index, 180 + ibft_eth_flags, 181 + ibft_eth_ip_addr, 182 + ibft_eth_subnet_mask, 183 + ibft_eth_origin, 184 + ibft_eth_gateway, 185 + ibft_eth_primary_dns, 186 + ibft_eth_secondary_dns, 187 + ibft_eth_dhcp, 188 + ibft_eth_vlan, 189 + ibft_eth_mac, 190 + /* ibft_eth_pci_bdf - this is replaced by link to the device itself. */ 191 + ibft_eth_hostname, 192 + ibft_eth_end_marker, 193 + }; 194 + 195 + static const char *ibft_eth_properties[] = 196 + {"index", "flags", "ip-addr", "subnet-mask", "origin", "gateway", 197 + "primary-dns", "secondary-dns", "dhcp", "vlan", "mac", "hostname", 198 + NULL}; 199 + 200 + enum ibft_tgt_properties_enum { 201 + ibft_tgt_index, 202 + ibft_tgt_flags, 203 + ibft_tgt_ip_addr, 204 + ibft_tgt_port, 205 + ibft_tgt_lun, 206 + ibft_tgt_chap_type, 207 + ibft_tgt_nic_assoc, 208 + ibft_tgt_name, 209 + ibft_tgt_chap_name, 210 + ibft_tgt_chap_secret, 211 + ibft_tgt_rev_chap_name, 212 + ibft_tgt_rev_chap_secret, 213 + ibft_tgt_end_marker, 214 + }; 215 + 216 + static const char *ibft_tgt_properties[] = 217 + {"index", "flags", "ip-addr", "port", "lun", "chap-type", "nic-assoc", 218 + "target-name", "chap-name", "chap-secret", "rev-chap-name", 219 + "rev-chap-name-secret", NULL}; 220 + 221 + enum ibft_initiator_properties_enum { 222 + ibft_init_index, 223 + ibft_init_flags, 224 + ibft_init_isns_server, 225 + ibft_init_slp_server, 226 + ibft_init_pri_radius_server, 227 + ibft_init_sec_radius_server, 228 + ibft_init_initiator_name, 229 + ibft_init_end_marker, 230 + }; 231 + 232 + static const char *ibft_initiator_properties[] = 233 + {"index", "flags", "isns-server", "slp-server", "pri-radius-server", 234 + "sec-radius-server", "initiator-name", NULL}; 235 + 236 + /* 237 + * The kobject and attribute structures. 238 + */ 239 + 240 + struct ibft_kobject { 241 + struct ibft_table_header *header; 242 + union { 243 + struct ibft_initiator *initiator; 244 + struct ibft_nic *nic; 245 + struct ibft_tgt *tgt; 246 + struct ibft_hdr *hdr; 247 + }; 248 + struct kobject kobj; 249 + struct list_head node; 250 + }; 251 + 252 + struct ibft_attribute { 253 + struct attribute attr; 254 + ssize_t (*show) (struct ibft_kobject *entry, 255 + struct ibft_attribute *attr, char *buf); 256 + union { 257 + struct ibft_initiator *initiator; 258 + struct ibft_nic *nic; 259 + struct ibft_tgt *tgt; 260 + struct ibft_hdr *hdr; 261 + }; 262 + struct kobject *kobj; 263 + int type; /* The enum of the type. This can be any value of: 264 + ibft_eth_properties_enum, ibft_tgt_properties_enum, 265 + or ibft_initiator_properties_enum. */ 266 + struct list_head node; 267 + }; 268 + 269 + static LIST_HEAD(ibft_attr_list); 270 + static LIST_HEAD(ibft_kobject_list); 271 + 272 + static const char nulls[16]; 273 + 274 + /* 275 + * Helper functions to parse data properly. 276 + */ 277 + static ssize_t sprintf_ipaddr(char *buf, u8 *ip) 278 + { 279 + char *str = buf; 280 + 281 + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && 282 + ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0 && 283 + ip[8] == 0 && ip[9] == 0 && ip[10] == 0xff && ip[11] == 0xff) { 284 + /* 285 + * IPV4 286 + */ 287 + str += sprintf(buf, NIPQUAD_FMT, ip[12], 288 + ip[13], ip[14], ip[15]); 289 + } else { 290 + /* 291 + * IPv6 292 + */ 293 + str += sprintf(str, NIP6_FMT, ntohs(ip[0]), ntohs(ip[1]), 294 + ntohs(ip[2]), ntohs(ip[3]), ntohs(ip[4]), 295 + ntohs(ip[5]), ntohs(ip[6]), ntohs(ip[7])); 296 + } 297 + str += sprintf(str, "\n"); 298 + return str - buf; 299 + } 300 + 301 + static ssize_t sprintf_string(char *str, int len, char *buf) 302 + { 303 + return sprintf(str, "%.*s\n", len, buf); 304 + } 305 + 306 + /* 307 + * Helper function to verify the IBFT header. 308 + */ 309 + static int ibft_verify_hdr(char *t, struct ibft_hdr *hdr, int id, int length) 310 + { 311 + if (hdr->id != id) { 312 + printk(KERN_ERR "iBFT error: We expected the " \ 313 + "field header.id to have %d but " \ 314 + "found %d instead!\n", id, hdr->id); 315 + return -ENODEV; 316 + } 317 + if (hdr->length != length) { 318 + printk(KERN_ERR "iBFT error: We expected the " \ 319 + "field header.length to have %d but " \ 320 + "found %d instead!\n", length, hdr->length); 321 + return -ENODEV; 322 + } 323 + 324 + return 0; 325 + } 326 + 327 + static void ibft_release(struct kobject *kobj) 328 + { 329 + struct ibft_kobject *ibft = 330 + container_of(kobj, struct ibft_kobject, kobj); 331 + kfree(ibft); 332 + } 333 + 334 + /* 335 + * Routines for parsing the iBFT data to be human readable. 336 + */ 337 + ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry, 338 + struct ibft_attribute *attr, 339 + char *buf) 340 + { 341 + struct ibft_initiator *initiator = entry->initiator; 342 + void *ibft_loc = entry->header; 343 + char *str = buf; 344 + 345 + if (!initiator) 346 + return 0; 347 + 348 + switch (attr->type) { 349 + case ibft_init_index: 350 + str += sprintf(str, "%d\n", initiator->hdr.index); 351 + break; 352 + case ibft_init_flags: 353 + str += sprintf(str, "%d\n", initiator->hdr.flags); 354 + break; 355 + case ibft_init_isns_server: 356 + str += sprintf_ipaddr(str, initiator->isns_server); 357 + break; 358 + case ibft_init_slp_server: 359 + str += sprintf_ipaddr(str, initiator->slp_server); 360 + break; 361 + case ibft_init_pri_radius_server: 362 + str += sprintf_ipaddr(str, initiator->pri_radius_server); 363 + break; 364 + case ibft_init_sec_radius_server: 365 + str += sprintf_ipaddr(str, initiator->sec_radius_server); 366 + break; 367 + case ibft_init_initiator_name: 368 + str += sprintf_string(str, initiator->initiator_name_len, 369 + (char *)ibft_loc + 370 + initiator->initiator_name_off); 371 + break; 372 + default: 373 + break; 374 + } 375 + 376 + return str - buf; 377 + } 378 + 379 + ssize_t ibft_attr_show_nic(struct ibft_kobject *entry, 380 + struct ibft_attribute *attr, 381 + char *buf) 382 + { 383 + struct ibft_nic *nic = entry->nic; 384 + void *ibft_loc = entry->header; 385 + char *str = buf; 386 + char *mac; 387 + int val; 388 + 389 + if (!nic) 390 + return 0; 391 + 392 + switch (attr->type) { 393 + case ibft_eth_index: 394 + str += sprintf(str, "%d\n", nic->hdr.index); 395 + break; 396 + case ibft_eth_flags: 397 + str += sprintf(str, "%d\n", nic->hdr.flags); 398 + break; 399 + case ibft_eth_ip_addr: 400 + str += sprintf_ipaddr(str, nic->ip_addr); 401 + break; 402 + case ibft_eth_subnet_mask: 403 + val = ~((1 << (32-nic->subnet_mask_prefix))-1); 404 + str += sprintf(str, NIPQUAD_FMT, 405 + (u8)(val >> 24), (u8)(val >> 16), 406 + (u8)(val >> 8), (u8)(val)); 407 + break; 408 + case ibft_eth_origin: 409 + str += sprintf(str, "%d\n", nic->origin); 410 + break; 411 + case ibft_eth_gateway: 412 + str += sprintf_ipaddr(str, nic->gateway); 413 + break; 414 + case ibft_eth_primary_dns: 415 + str += sprintf_ipaddr(str, nic->primary_dns); 416 + break; 417 + case ibft_eth_secondary_dns: 418 + str += sprintf_ipaddr(str, nic->secondary_dns); 419 + break; 420 + case ibft_eth_dhcp: 421 + str += sprintf_ipaddr(str, nic->dhcp); 422 + break; 423 + case ibft_eth_vlan: 424 + str += sprintf(str, "%d\n", nic->vlan); 425 + break; 426 + case ibft_eth_mac: 427 + mac = nic->mac; 428 + str += sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x\n", 429 + (u8)mac[0], (u8)mac[1], (u8)mac[2], 430 + (u8)mac[3], (u8)mac[4], (u8)mac[5]); 431 + break; 432 + case ibft_eth_hostname: 433 + str += sprintf_string(str, nic->hostname_len, 434 + (char *)ibft_loc + nic->hostname_off); 435 + break; 436 + default: 437 + break; 438 + } 439 + 440 + return str - buf; 441 + }; 442 + 443 + ssize_t ibft_attr_show_target(struct ibft_kobject *entry, 444 + struct ibft_attribute *attr, 445 + char *buf) 446 + { 447 + struct ibft_tgt *tgt = entry->tgt; 448 + void *ibft_loc = entry->header; 449 + char *str = buf; 450 + int i; 451 + 452 + if (!tgt) 453 + return 0; 454 + 455 + switch (attr->type) { 456 + case ibft_tgt_index: 457 + str += sprintf(str, "%d\n", tgt->hdr.index); 458 + break; 459 + case ibft_tgt_flags: 460 + str += sprintf(str, "%d\n", tgt->hdr.flags); 461 + break; 462 + case ibft_tgt_ip_addr: 463 + str += sprintf_ipaddr(str, tgt->ip_addr); 464 + break; 465 + case ibft_tgt_port: 466 + str += sprintf(str, "%d\n", tgt->port); 467 + break; 468 + case ibft_tgt_lun: 469 + for (i = 0; i < 8; i++) 470 + str += sprintf(str, "%x", (u8)tgt->lun[i]); 471 + str += sprintf(str, "\n"); 472 + break; 473 + case ibft_tgt_nic_assoc: 474 + str += sprintf(str, "%d\n", tgt->nic_assoc); 475 + break; 476 + case ibft_tgt_chap_type: 477 + str += sprintf(str, "%d\n", tgt->chap_type); 478 + break; 479 + case ibft_tgt_name: 480 + str += sprintf_string(str, tgt->tgt_name_len, 481 + (char *)ibft_loc + tgt->tgt_name_off); 482 + break; 483 + case ibft_tgt_chap_name: 484 + str += sprintf_string(str, tgt->chap_name_len, 485 + (char *)ibft_loc + tgt->chap_name_off); 486 + break; 487 + case ibft_tgt_chap_secret: 488 + str += sprintf_string(str, tgt->chap_secret_len, 489 + (char *)ibft_loc + tgt->chap_secret_off); 490 + break; 491 + case ibft_tgt_rev_chap_name: 492 + str += sprintf_string(str, tgt->rev_chap_name_len, 493 + (char *)ibft_loc + 494 + tgt->rev_chap_name_off); 495 + break; 496 + case ibft_tgt_rev_chap_secret: 497 + str += sprintf_string(str, tgt->rev_chap_secret_len, 498 + (char *)ibft_loc + 499 + tgt->rev_chap_secret_off); 500 + break; 501 + default: 502 + break; 503 + } 504 + 505 + return str - buf; 506 + } 507 + 508 + /* 509 + * The routine called for all sysfs attributes. 510 + */ 511 + static ssize_t ibft_show_attribute(struct kobject *kobj, 512 + struct attribute *attr, 513 + char *buf) 514 + { 515 + struct ibft_kobject *dev = 516 + container_of(kobj, struct ibft_kobject, kobj); 517 + struct ibft_attribute *ibft_attr = 518 + container_of(attr, struct ibft_attribute, attr); 519 + ssize_t ret = -EIO; 520 + char *str = buf; 521 + 522 + if (!capable(CAP_SYS_ADMIN)) 523 + return -EACCES; 524 + 525 + if (ibft_attr->show) 526 + ret = ibft_attr->show(dev, ibft_attr, str); 527 + 528 + return ret; 529 + } 530 + 531 + static struct sysfs_ops ibft_attr_ops = { 532 + .show = ibft_show_attribute, 533 + }; 534 + 535 + static struct kobj_type ibft_ktype = { 536 + .release = ibft_release, 537 + .sysfs_ops = &ibft_attr_ops, 538 + }; 539 + 540 + static struct kset *ibft_kset; 541 + 542 + static int __init ibft_check_device(void) 543 + { 544 + int len; 545 + u8 *pos; 546 + u8 csum = 0; 547 + 548 + len = ibft_addr->length; 549 + 550 + /* Sanity checking of iBFT. */ 551 + if (ibft_addr->revision != 1) { 552 + printk(KERN_ERR "iBFT module supports only revision 1, " \ 553 + "while this is %d.\n", ibft_addr->revision); 554 + return -ENOENT; 555 + } 556 + for (pos = (u8 *)ibft_addr; pos < (u8 *)ibft_addr + len; pos++) 557 + csum += *pos; 558 + 559 + if (csum) { 560 + printk(KERN_ERR "iBFT has incorrect checksum (0x%x)!\n", csum); 561 + return -ENOENT; 562 + } 563 + 564 + return 0; 565 + } 566 + 567 + /* 568 + * Helper function for ibft_register_kobjects. 569 + */ 570 + static int __init ibft_create_kobject(struct ibft_table_header *header, 571 + struct ibft_hdr *hdr, 572 + struct list_head *list) 573 + { 574 + struct ibft_kobject *ibft_kobj = NULL; 575 + struct ibft_nic *nic = (struct ibft_nic *)hdr; 576 + struct pci_dev *pci_dev; 577 + int rc = 0; 578 + 579 + ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL); 580 + if (!ibft_kobj) 581 + return -ENOMEM; 582 + 583 + ibft_kobj->header = header; 584 + ibft_kobj->hdr = hdr; 585 + 586 + switch (hdr->id) { 587 + case id_initiator: 588 + rc = ibft_verify_hdr("initiator", hdr, id_initiator, 589 + sizeof(*ibft_kobj->initiator)); 590 + break; 591 + case id_nic: 592 + rc = ibft_verify_hdr("ethernet", hdr, id_nic, 593 + sizeof(*ibft_kobj->nic)); 594 + break; 595 + case id_target: 596 + rc = ibft_verify_hdr("target", hdr, id_target, 597 + sizeof(*ibft_kobj->tgt)); 598 + break; 599 + case id_reserved: 600 + case id_control: 601 + case id_extensions: 602 + /* Fields which we don't support. Ignore them */ 603 + rc = 1; 604 + break; 605 + default: 606 + printk(KERN_ERR "iBFT has unknown structure type (%d). " \ 607 + "Report this bug to %.6s!\n", hdr->id, 608 + header->oem_id); 609 + rc = 1; 610 + break; 611 + } 612 + 613 + if (rc) { 614 + /* Skip adding this kobject, but exit with non-fatal error. */ 615 + kfree(ibft_kobj); 616 + goto out_invalid_struct; 617 + } 618 + 619 + ibft_kobj->kobj.kset = ibft_kset; 620 + 621 + rc = kobject_init_and_add(&ibft_kobj->kobj, &ibft_ktype, 622 + NULL, ibft_id_names[hdr->id], hdr->index); 623 + 624 + if (rc) { 625 + kfree(ibft_kobj); 626 + goto out; 627 + } 628 + 629 + kobject_uevent(&ibft_kobj->kobj, KOBJ_ADD); 630 + 631 + if (hdr->id == id_nic) { 632 + /* 633 + * We don't search for the device in other domains than 634 + * zero. This is because on x86 platforms the BIOS 635 + * executes only devices which are in domain 0. Furthermore, the 636 + * iBFT spec doesn't have a domain id field :-( 637 + */ 638 + pci_dev = pci_get_bus_and_slot((nic->pci_bdf & 0xff00) >> 8, 639 + (nic->pci_bdf & 0xff)); 640 + if (pci_dev) { 641 + rc = sysfs_create_link(&ibft_kobj->kobj, 642 + &pci_dev->dev.kobj, "device"); 643 + pci_dev_put(pci_dev); 644 + } 645 + } 646 + 647 + /* Nothing broke so lets add it to the list. */ 648 + list_add_tail(&ibft_kobj->node, list); 649 + out: 650 + return rc; 651 + out_invalid_struct: 652 + /* Unsupported structs are skipped. */ 653 + return 0; 654 + } 655 + 656 + /* 657 + * Scan the IBFT table structure for the NIC and Target fields. When 658 + * found add them on the passed-in list. We do not support the other 659 + * fields at this point, so they are skipped. 660 + */ 661 + static int __init ibft_register_kobjects(struct ibft_table_header *header, 662 + struct list_head *list) 663 + { 664 + struct ibft_control *control = NULL; 665 + void *ptr, *end; 666 + int rc = 0; 667 + u16 offset; 668 + u16 eot_offset; 669 + 670 + control = (void *)header + sizeof(*header); 671 + end = (void *)control + control->hdr.length; 672 + eot_offset = (void *)header + header->length - 673 + (void *)control - sizeof(*header); 674 + rc = ibft_verify_hdr("control", (struct ibft_hdr *)control, id_control, 675 + sizeof(*control)); 676 + 677 + /* iBFT table safety checking */ 678 + rc |= ((control->hdr.index) ? -ENODEV : 0); 679 + if (rc) { 680 + printk(KERN_ERR "iBFT error: Control header is invalid!\n"); 681 + return rc; 682 + } 683 + for (ptr = &control->initiator_off; ptr < end; ptr += sizeof(u16)) { 684 + offset = *(u16 *)ptr; 685 + if (offset && offset < header->length && offset < eot_offset) { 686 + rc = ibft_create_kobject(header, 687 + (void *)header + offset, 688 + list); 689 + if (rc) 690 + break; 691 + } 692 + } 693 + 694 + return rc; 695 + } 696 + 697 + static void ibft_unregister(struct list_head *attr_list, 698 + struct list_head *kobj_list) 699 + { 700 + struct ibft_kobject *data = NULL, *n; 701 + struct ibft_attribute *attr = NULL, *m; 702 + 703 + list_for_each_entry_safe(attr, m, attr_list, node) { 704 + sysfs_remove_file(attr->kobj, &attr->attr); 705 + list_del(&attr->node); 706 + kfree(attr); 707 + }; 708 + list_del_init(attr_list); 709 + 710 + list_for_each_entry_safe(data, n, kobj_list, node) { 711 + list_del(&data->node); 712 + if (data->hdr->id == id_nic) 713 + sysfs_remove_link(&data->kobj, "device"); 714 + kobject_put(&data->kobj); 715 + }; 716 + list_del_init(kobj_list); 717 + } 718 + 719 + static int __init ibft_create_attribute(struct ibft_kobject *kobj_data, 720 + int type, 721 + const char *name, 722 + ssize_t (*show)(struct ibft_kobject *, 723 + struct ibft_attribute*, 724 + char *buf), 725 + struct list_head *list) 726 + { 727 + struct ibft_attribute *attr = NULL; 728 + struct ibft_hdr *hdr = kobj_data->hdr; 729 + 730 + attr = kmalloc(sizeof(*attr), GFP_KERNEL); 731 + if (!attr) 732 + return -ENOMEM; 733 + 734 + attr->attr.name = name; 735 + attr->attr.mode = S_IRUSR; 736 + attr->attr.owner = THIS_MODULE; 737 + 738 + attr->hdr = hdr; 739 + attr->show = show; 740 + attr->kobj = &kobj_data->kobj; 741 + attr->type = type; 742 + 743 + list_add_tail(&attr->node, list); 744 + 745 + return 0; 746 + } 747 + 748 + /* 749 + * Helper routiners to check to determine if the entry is valid 750 + * in the proper iBFT structure. 751 + */ 752 + static int __init ibft_check_nic_for(struct ibft_nic *nic, int entry) 753 + { 754 + int rc = 0; 755 + 756 + switch (entry) { 757 + case ibft_eth_index: 758 + case ibft_eth_flags: 759 + rc = 1; 760 + break; 761 + case ibft_eth_ip_addr: 762 + if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) 763 + rc = 1; 764 + break; 765 + case ibft_eth_subnet_mask: 766 + if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) 767 + rc = 1; 768 + break; 769 + case ibft_eth_origin: 770 + rc = 1; 771 + break; 772 + case ibft_eth_gateway: 773 + if (memcmp(nic->gateway, nulls, sizeof(nic->gateway))) 774 + rc = 1; 775 + break; 776 + case ibft_eth_primary_dns: 777 + if (memcmp(nic->primary_dns, nulls, 778 + sizeof(nic->primary_dns))) 779 + rc = 1; 780 + break; 781 + case ibft_eth_secondary_dns: 782 + if (memcmp(nic->secondary_dns, nulls, 783 + sizeof(nic->secondary_dns))) 784 + rc = 1; 785 + break; 786 + case ibft_eth_dhcp: 787 + if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) 788 + rc = 1; 789 + break; 790 + case ibft_eth_vlan: 791 + case ibft_eth_mac: 792 + rc = 1; 793 + break; 794 + case ibft_eth_hostname: 795 + if (nic->hostname_off) 796 + rc = 1; 797 + break; 798 + default: 799 + break; 800 + } 801 + 802 + return rc; 803 + } 804 + 805 + static int __init ibft_check_tgt_for(struct ibft_tgt *tgt, int entry) 806 + { 807 + int rc = 0; 808 + 809 + switch (entry) { 810 + case ibft_tgt_index: 811 + case ibft_tgt_flags: 812 + case ibft_tgt_ip_addr: 813 + case ibft_tgt_port: 814 + case ibft_tgt_lun: 815 + case ibft_tgt_nic_assoc: 816 + case ibft_tgt_chap_type: 817 + rc = 1; 818 + case ibft_tgt_name: 819 + if (tgt->tgt_name_len) 820 + rc = 1; 821 + break; 822 + case ibft_tgt_chap_name: 823 + case ibft_tgt_chap_secret: 824 + if (tgt->chap_name_len) 825 + rc = 1; 826 + break; 827 + case ibft_tgt_rev_chap_name: 828 + case ibft_tgt_rev_chap_secret: 829 + if (tgt->rev_chap_name_len) 830 + rc = 1; 831 + break; 832 + default: 833 + break; 834 + } 835 + 836 + return rc; 837 + } 838 + 839 + static int __init ibft_check_initiator_for(struct ibft_initiator *init, 840 + int entry) 841 + { 842 + int rc = 0; 843 + 844 + switch (entry) { 845 + case ibft_init_index: 846 + case ibft_init_flags: 847 + rc = 1; 848 + break; 849 + case ibft_init_isns_server: 850 + if (memcmp(init->isns_server, nulls, 851 + sizeof(init->isns_server))) 852 + rc = 1; 853 + break; 854 + case ibft_init_slp_server: 855 + if (memcmp(init->slp_server, nulls, 856 + sizeof(init->slp_server))) 857 + rc = 1; 858 + break; 859 + case ibft_init_pri_radius_server: 860 + if (memcmp(init->pri_radius_server, nulls, 861 + sizeof(init->pri_radius_server))) 862 + rc = 1; 863 + break; 864 + case ibft_init_sec_radius_server: 865 + if (memcmp(init->sec_radius_server, nulls, 866 + sizeof(init->sec_radius_server))) 867 + rc = 1; 868 + break; 869 + case ibft_init_initiator_name: 870 + if (init->initiator_name_len) 871 + rc = 1; 872 + break; 873 + default: 874 + break; 875 + } 876 + 877 + return rc; 878 + } 879 + 880 + /* 881 + * Register the attributes for all of the kobjects. 882 + */ 883 + static int __init ibft_register_attributes(struct list_head *kobject_list, 884 + struct list_head *attr_list) 885 + { 886 + int rc = 0, i = 0; 887 + struct ibft_kobject *data = NULL; 888 + struct ibft_attribute *attr = NULL, *m; 889 + 890 + list_for_each_entry(data, kobject_list, node) { 891 + switch (data->hdr->id) { 892 + case id_nic: 893 + for (i = 0; i < ibft_eth_end_marker && !rc; i++) 894 + if (ibft_check_nic_for(data->nic, i)) 895 + rc = ibft_create_attribute(data, i, 896 + ibft_eth_properties[i], 897 + ibft_attr_show_nic, attr_list); 898 + break; 899 + case id_target: 900 + for (i = 0; i < ibft_tgt_end_marker && !rc; i++) 901 + if (ibft_check_tgt_for(data->tgt, i)) 902 + rc = ibft_create_attribute(data, i, 903 + ibft_tgt_properties[i], 904 + ibft_attr_show_target, 905 + attr_list); 906 + break; 907 + case id_initiator: 908 + for (i = 0; i < ibft_init_end_marker && !rc; i++) 909 + if (ibft_check_initiator_for( 910 + data->initiator, i)) 911 + rc = ibft_create_attribute(data, i, 912 + ibft_initiator_properties[i], 913 + ibft_attr_show_initiator, 914 + attr_list); 915 + break; 916 + default: 917 + break; 918 + } 919 + if (rc) 920 + break; 921 + } 922 + list_for_each_entry_safe(attr, m, attr_list, node) { 923 + rc = sysfs_create_file(attr->kobj, &attr->attr); 924 + if (rc) { 925 + list_del(&attr->node); 926 + kfree(attr); 927 + break; 928 + } 929 + } 930 + 931 + return rc; 932 + } 933 + 934 + /* 935 + * ibft_init() - creates sysfs tree entries for the iBFT data. 936 + */ 937 + static int __init ibft_init(void) 938 + { 939 + int rc = 0; 940 + 941 + ibft_kset = kset_create_and_add("ibft", NULL, firmware_kobj); 942 + if (!ibft_kset) 943 + return -ENOMEM; 944 + 945 + if (ibft_addr) { 946 + printk(KERN_INFO "iBFT detected at 0x%lx.\n", 947 + virt_to_phys((void *)ibft_addr)); 948 + 949 + rc = ibft_check_device(); 950 + if (rc) 951 + goto out_firmware_unregister; 952 + 953 + /* Scan the IBFT for data and register the kobjects. */ 954 + rc = ibft_register_kobjects(ibft_addr, &ibft_kobject_list); 955 + if (rc) 956 + goto out_free; 957 + 958 + /* Register the attributes */ 959 + rc = ibft_register_attributes(&ibft_kobject_list, 960 + &ibft_attr_list); 961 + if (rc) 962 + goto out_free; 963 + } else 964 + printk(KERN_INFO "No iBFT detected.\n"); 965 + 966 + return 0; 967 + 968 + out_free: 969 + ibft_unregister(&ibft_attr_list, &ibft_kobject_list); 970 + out_firmware_unregister: 971 + kset_unregister(ibft_kset); 972 + return rc; 973 + } 974 + 975 + static void __exit ibft_exit(void) 976 + { 977 + ibft_unregister(&ibft_attr_list, &ibft_kobject_list); 978 + kset_unregister(ibft_kset); 979 + } 980 + 981 + module_init(ibft_init); 982 + module_exit(ibft_exit);
+84
drivers/firmware/iscsi_ibft_find.c
··· 1 + /* 2 + * Copyright 2007 Red Hat, Inc. 3 + * by Peter Jones <pjones@redhat.com> 4 + * Copyright 2007 IBM, Inc. 5 + * by Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 6 + * Copyright 2008 7 + * by Konrad Rzeszutek <ketuzsezr@darnok.org> 8 + * 9 + * This code finds the iSCSI Boot Format Table. 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of the GNU General Public License v2.0 as published by 13 + * the Free Software Foundation 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + */ 20 + 21 + #include <linux/bootmem.h> 22 + #include <linux/blkdev.h> 23 + #include <linux/ctype.h> 24 + #include <linux/device.h> 25 + #include <linux/err.h> 26 + #include <linux/init.h> 27 + #include <linux/limits.h> 28 + #include <linux/module.h> 29 + #include <linux/pci.h> 30 + #include <linux/slab.h> 31 + #include <linux/stat.h> 32 + #include <linux/string.h> 33 + #include <linux/types.h> 34 + 35 + #include <asm/mmzone.h> 36 + 37 + /* 38 + * Physical location of iSCSI Boot Format Table. 39 + */ 40 + struct ibft_table_header *ibft_addr; 41 + EXPORT_SYMBOL_GPL(ibft_addr); 42 + 43 + #define IBFT_SIGN "iBFT" 44 + #define IBFT_SIGN_LEN 4 45 + #define IBFT_START 0x80000 /* 512kB */ 46 + #define IBFT_END 0x100000 /* 1MB */ 47 + #define VGA_MEM 0xA0000 /* VGA buffer */ 48 + #define VGA_SIZE 0x20000 /* 128kB */ 49 + 50 + 51 + /* 52 + * Routine used to find the iSCSI Boot Format Table. The logical 53 + * kernel address is set in the ibft_addr global variable. 54 + */ 55 + void __init reserve_ibft_region(void) 56 + { 57 + unsigned long pos; 58 + unsigned int len = 0; 59 + void *virt; 60 + 61 + ibft_addr = 0; 62 + 63 + for (pos = IBFT_START; pos < IBFT_END; pos += 16) { 64 + /* The table can't be inside the VGA BIOS reserved space, 65 + * so skip that area */ 66 + if (pos == VGA_MEM) 67 + pos += VGA_SIZE; 68 + virt = phys_to_virt(pos); 69 + if (memcmp(virt, IBFT_SIGN, IBFT_SIGN_LEN) == 0) { 70 + unsigned long *addr = 71 + (unsigned long *)phys_to_virt(pos + 4); 72 + len = *addr; 73 + /* if the length of the table extends past 1M, 74 + * the table cannot be valid. */ 75 + if (pos + len <= (IBFT_END-1)) { 76 + ibft_addr = (struct ibft_table_header *)virt; 77 + break; 78 + } 79 + } 80 + } 81 + if (ibft_addr) 82 + reserve_bootmem(pos, PAGE_ALIGN(len), BOOTMEM_DEFAULT); 83 + } 84 + EXPORT_SYMBOL_GPL(reserve_ibft_region);
+50
include/linux/iscsi_ibft.h
··· 1 + /* 2 + * Copyright 2007 Red Hat, Inc. 3 + * by Peter Jones <pjones@redhat.com> 4 + * Copyright 2007 IBM, Inc. 5 + * by Konrad Rzeszutek <konradr@linux.vnet.ibm.com> 6 + * Copyright 2008 7 + * by Konrad Rzeszutek <ketuzsezr@darnok.org> 8 + * 9 + * This code exposes the iSCSI Boot Format Table to userland via sysfs. 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of the GNU General Public License v2.0 as published by 13 + * the Free Software Foundation 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + */ 20 + 21 + #ifndef ISCSI_IBFT_H 22 + #define ISCSI_IBFT_H 23 + 24 + struct ibft_table_header { 25 + char signature[4]; 26 + u32 length; 27 + u8 revision; 28 + u8 checksum; 29 + char oem_id[6]; 30 + char oem_table_id[8]; 31 + char reserved[24]; 32 + } __attribute__((__packed__)); 33 + 34 + /* 35 + * Logical location of iSCSI Boot Format Table. 36 + * If the value is NULL there is no iBFT on the machine. 37 + */ 38 + extern struct ibft_table_header *ibft_addr; 39 + 40 + /* 41 + * Routine used to find and reserve the iSCSI Boot Format Table. The 42 + * mapped address is set in the ibft_addr variable. 43 + */ 44 + #ifdef CONFIG_ISCSI_IBFT_FIND 45 + extern void __init reserve_ibft_region(void); 46 + #else 47 + static inline void reserve_ibft_region(void) { } 48 + #endif 49 + 50 + #endif /* ISCSI_IBFT_H */