linux/drivers/firmware/iscsi_ibft.c
<<
>>
Prefs
   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 */
 337static 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
 379static 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
 443static 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 */
 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 - (void *)control;
 673        rc = ibft_verify_hdr("control", (struct ibft_hdr *)control, id_control,
 674                             sizeof(*control));
 675
 676        /* iBFT table safety checking */
 677        rc |= ((control->hdr.index) ? -ENODEV : 0);
 678        if (rc) {
 679                printk(KERN_ERR "iBFT error: Control header is invalid!\n");
 680                return rc;
 681        }
 682        for (ptr = &control->initiator_off; ptr < end; ptr += sizeof(u16)) {
 683                offset = *(u16 *)ptr;
 684                if (offset && offset < header->length && offset < eot_offset) {
 685                        rc = ibft_create_kobject(header,
 686                                                 (void *)header + offset,
 687                                                 list);
 688                        if (rc)
 689                                break;
 690                }
 691        }
 692
 693        return rc;
 694}
 695
 696static void ibft_unregister(struct list_head *attr_list,
 697                             struct list_head *kobj_list)
 698{
 699        struct ibft_kobject *data = NULL, *n;
 700        struct ibft_attribute *attr = NULL, *m;
 701
 702        list_for_each_entry_safe(attr, m, attr_list, node) {
 703                sysfs_remove_file(attr->kobj, &attr->attr);
 704                list_del(&attr->node);
 705                kfree(attr);
 706        };
 707        list_del_init(attr_list);
 708
 709        list_for_each_entry_safe(data, n, kobj_list, node) {
 710                list_del(&data->node);
 711                if (data->hdr->id == id_nic)
 712                        sysfs_remove_link(&data->kobj, "device");
 713                kobject_put(&data->kobj);
 714        };
 715        list_del_init(kobj_list);
 716}
 717
 718static int __init ibft_create_attribute(struct ibft_kobject *kobj_data,
 719                                         int type,
 720                                         const char *name,
 721                                         ssize_t (*show)(struct ibft_kobject *,
 722                                                         struct ibft_attribute*,
 723                                                         char *buf),
 724                                         struct list_head *list)
 725{
 726        struct ibft_attribute *attr = NULL;
 727        struct ibft_hdr *hdr = kobj_data->hdr;
 728
 729        attr = kmalloc(sizeof(*attr), GFP_KERNEL);
 730        if (!attr)
 731                return -ENOMEM;
 732
 733        attr->attr.name = name;
 734        attr->attr.mode = S_IRUSR;
 735
 736        attr->hdr = hdr;
 737        attr->show = show;
 738        attr->kobj = &kobj_data->kobj;
 739        attr->type = type;
 740
 741        list_add_tail(&attr->node, list);
 742
 743        return 0;
 744}
 745
 746/*
 747 * Helper routiners to check to determine if the entry is valid
 748 * in the proper iBFT structure.
 749 */
 750static int __init ibft_check_nic_for(struct ibft_nic *nic, int entry)
 751{
 752        int rc = 0;
 753
 754        switch (entry) {
 755        case ibft_eth_index:
 756        case ibft_eth_flags:
 757                rc = 1;
 758                break;
 759        case ibft_eth_ip_addr:
 760                if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
 761                        rc = 1;
 762                break;
 763        case ibft_eth_subnet_mask:
 764                if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
 765                        rc = 1;
 766                break;
 767        case ibft_eth_origin:
 768                rc = 1;
 769                break;
 770        case ibft_eth_gateway:
 771                if (memcmp(nic->gateway, nulls, sizeof(nic->gateway)))
 772                        rc = 1;
 773                break;
 774        case ibft_eth_primary_dns:
 775                if (memcmp(nic->primary_dns, nulls,
 776                           sizeof(nic->primary_dns)))
 777                        rc = 1;
 778                break;
 779        case ibft_eth_secondary_dns:
 780                if (memcmp(nic->secondary_dns, nulls,
 781                           sizeof(nic->secondary_dns)))
 782                        rc = 1;
 783                break;
 784        case ibft_eth_dhcp:
 785                if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
 786                        rc = 1;
 787                break;
 788        case ibft_eth_vlan:
 789        case ibft_eth_mac:
 790                rc = 1;
 791                break;
 792        case ibft_eth_hostname:
 793                if (nic->hostname_off)
 794                        rc = 1;
 795                break;
 796        default:
 797                break;
 798        }
 799
 800        return rc;
 801}
 802
 803static int __init ibft_check_tgt_for(struct ibft_tgt *tgt, int entry)
 804{
 805        int rc = 0;
 806
 807        switch (entry) {
 808        case ibft_tgt_index:
 809        case ibft_tgt_flags:
 810        case ibft_tgt_ip_addr:
 811        case ibft_tgt_port:
 812        case ibft_tgt_lun:
 813        case ibft_tgt_nic_assoc:
 814        case ibft_tgt_chap_type:
 815                rc = 1;
 816        case ibft_tgt_name:
 817                if (tgt->tgt_name_len)
 818                        rc = 1;
 819                break;
 820        case ibft_tgt_chap_name:
 821        case ibft_tgt_chap_secret:
 822                if (tgt->chap_name_len)
 823                        rc = 1;
 824                break;
 825        case ibft_tgt_rev_chap_name:
 826        case ibft_tgt_rev_chap_secret:
 827                if (tgt->rev_chap_name_len)
 828                        rc = 1;
 829                break;
 830        default:
 831                break;
 832        }
 833
 834        return rc;
 835}
 836
 837static int __init ibft_check_initiator_for(struct ibft_initiator *init,
 838                                            int entry)
 839{
 840        int rc = 0;
 841
 842        switch (entry) {
 843        case ibft_init_index:
 844        case ibft_init_flags:
 845                rc = 1;
 846                break;
 847        case ibft_init_isns_server:
 848                if (memcmp(init->isns_server, nulls,
 849                           sizeof(init->isns_server)))
 850                        rc = 1;
 851                break;
 852        case ibft_init_slp_server:
 853                if (memcmp(init->slp_server, nulls,
 854                           sizeof(init->slp_server)))
 855                        rc = 1;
 856                break;
 857        case ibft_init_pri_radius_server:
 858                if (memcmp(init->pri_radius_server, nulls,
 859                           sizeof(init->pri_radius_server)))
 860                        rc = 1;
 861                break;
 862        case ibft_init_sec_radius_server:
 863                if (memcmp(init->sec_radius_server, nulls,
 864                           sizeof(init->sec_radius_server)))
 865                        rc = 1;
 866                break;
 867        case ibft_init_initiator_name:
 868                if (init->initiator_name_len)
 869                        rc = 1;
 870                break;
 871        default:
 872                break;
 873        }
 874
 875        return rc;
 876}
 877
 878/*
 879 *  Register the attributes for all of the kobjects.
 880 */
 881static int __init ibft_register_attributes(struct list_head *kobject_list,
 882                                            struct list_head *attr_list)
 883{
 884        int rc = 0, i = 0;
 885        struct ibft_kobject *data = NULL;
 886        struct ibft_attribute *attr = NULL, *m;
 887
 888        list_for_each_entry(data, kobject_list, node) {
 889                switch (data->hdr->id) {
 890                case id_nic:
 891                        for (i = 0; i < ibft_eth_end_marker && !rc; i++)
 892                                if (ibft_check_nic_for(data->nic, i))
 893                                        rc = ibft_create_attribute(data, i,
 894                                                ibft_eth_properties[i],
 895                                                ibft_attr_show_nic, attr_list);
 896                        break;
 897                case id_target:
 898                        for (i = 0; i < ibft_tgt_end_marker && !rc; i++)
 899                                if (ibft_check_tgt_for(data->tgt, i))
 900                                        rc = ibft_create_attribute(data, i,
 901                                                ibft_tgt_properties[i],
 902                                                ibft_attr_show_target,
 903                                                attr_list);
 904                        break;
 905                case id_initiator:
 906                        for (i = 0; i < ibft_init_end_marker && !rc; i++)
 907                                if (ibft_check_initiator_for(
 908                                        data->initiator, i))
 909                                        rc = ibft_create_attribute(data, i,
 910                                                ibft_initiator_properties[i],
 911                                                ibft_attr_show_initiator,
 912                                                attr_list);
 913                        break;
 914                default:
 915                        break;
 916                }
 917                if (rc)
 918                        break;
 919        }
 920        list_for_each_entry_safe(attr, m, attr_list, node) {
 921                rc = sysfs_create_file(attr->kobj, &attr->attr);
 922                if (rc) {
 923                        list_del(&attr->node);
 924                        kfree(attr);
 925                        break;
 926                }
 927        }
 928
 929        return rc;
 930}
 931
 932/*
 933 * ibft_init() - creates sysfs tree entries for the iBFT data.
 934 */
 935static int __init ibft_init(void)
 936{
 937        int rc = 0;
 938
 939        ibft_kset = kset_create_and_add("ibft", NULL, firmware_kobj);
 940        if (!ibft_kset)
 941                return -ENOMEM;
 942
 943        if (ibft_addr) {
 944                printk(KERN_INFO "iBFT detected at 0x%lx.\n",
 945                       virt_to_phys((void *)ibft_addr));
 946
 947                rc = ibft_check_device();
 948                if (rc)
 949                        goto out_firmware_unregister;
 950
 951                /* Scan the IBFT for data and register the kobjects. */
 952                rc = ibft_register_kobjects(ibft_addr, &ibft_kobject_list);
 953                if (rc)
 954                        goto out_free;
 955
 956                /* Register the attributes */
 957                rc = ibft_register_attributes(&ibft_kobject_list,
 958                                              &ibft_attr_list);
 959                if (rc)
 960                        goto out_free;
 961        } else
 962                printk(KERN_INFO "No iBFT detected.\n");
 963
 964        return 0;
 965
 966out_free:
 967        ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
 968out_firmware_unregister:
 969        kset_unregister(ibft_kset);
 970        return rc;
 971}
 972
 973static void __exit ibft_exit(void)
 974{
 975        ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
 976        kset_unregister(ibft_kset);
 977}
 978
 979module_init(ibft_init);
 980module_exit(ibft_exit);
 981