aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKonrad Rzeszutek <ketuzsezr@darnok.org>2008-04-09 22:50:41 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-19 22:10:28 -0400
commit138fe4e069798d9aa948a5402ff15e58f483ee4e (patch)
tree413ab0c86618df7dba7724c1945fd46cd33298b9 /drivers
parent95bc6a10830de469eee94c17fb1c37b3b1430715 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firmware/Kconfig20
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/iscsi_ibft.c982
-rw-r--r--drivers/firmware/iscsi_ibft_find.c84
4 files changed, 1088 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 05f02a326f1c..40ffd767647d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -93,4 +93,24 @@ config DMIID
93 information from userspace through /sys/class/dmi/id/ or if you want 93 information from userspace through /sys/class/dmi/id/ or if you want
94 DMI-based module auto-loading. 94 DMI-based module auto-loading.
95 95
96config 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
106config 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
96endmenu 116endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 8d4ebc805a50..4c9147154df8 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_EFI_PCDP) += pcdp.o
8obj-$(CONFIG_DELL_RBU) += dell_rbu.o 8obj-$(CONFIG_DELL_RBU) += dell_rbu.o
9obj-$(CONFIG_DCDBAS) += dcdbas.o 9obj-$(CONFIG_DCDBAS) += dcdbas.o
10obj-$(CONFIG_DMIID) += dmi-id.o 10obj-$(CONFIG_DMIID) += dmi-id.o
11obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
12obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
new file mode 100644
index 000000000000..8024e3bfd877
--- /dev/null
+++ b/drivers/firmware/iscsi_ibft.c
@@ -0,0 +1,982 @@
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
85MODULE_AUTHOR("Peter Jones <pjones@redhat.com> and \
86Konrad Rzeszutek <ketuzsezr@darnok.org>");
87MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information");
88MODULE_LICENSE("GPL");
89MODULE_VERSION(IBFT_ISCSI_VERSION);
90
91struct ibft_hdr {
92 u8 id;
93 u8 version;
94 u16 length;
95 u8 index;
96 u8 flags;
97} __attribute__((__packed__));
98
99struct 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
109struct 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
119struct 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
135struct 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*/
158enum 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 */
172static 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*/
178enum 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
195static 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
200enum 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
216static 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
221enum 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
232static 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
240struct 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
252struct 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
269static LIST_HEAD(ibft_attr_list);
270static LIST_HEAD(ibft_kobject_list);
271
272static const char nulls[16];
273
274/*
275 * Helper functions to parse data properly.
276 */
277static 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
301static 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 */
309static 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
327static 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 */
337ssize_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
379ssize_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
443ssize_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 */
511static 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
531static struct sysfs_ops ibft_attr_ops = {
532 .show = ibft_show_attribute,
533};
534
535static struct kobj_type ibft_ktype = {
536 .release = ibft_release,
537 .sysfs_ops = &ibft_attr_ops,
538};
539
540static struct kset *ibft_kset;
541
542static 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 */
570static 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);
649out:
650 return rc;
651out_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 */
661static 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
697static 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
719static 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 */
752static 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
805static 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
839static 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 */
883static 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 */
937static 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
968out_free:
969 ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
970out_firmware_unregister:
971 kset_unregister(ibft_kset);
972 return rc;
973}
974
975static void __exit ibft_exit(void)
976{
977 ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
978 kset_unregister(ibft_kset);
979}
980
981module_init(ibft_init);
982module_exit(ibft_exit);
diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c
new file mode 100644
index 000000000000..d0e5fa4ea51b
--- /dev/null
+++ b/drivers/firmware/iscsi_ibft_find.c
@@ -0,0 +1,84 @@
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 */
40struct ibft_table_header *ibft_addr;
41EXPORT_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 */
55void __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}
84EXPORT_SYMBOL_GPL(reserve_ibft_region);