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, "%pI4", ip + 12);
 288        } else {
 289                /*
 290                 * IPv6
 291                 */
 292                str += sprintf(str, "%pI6", ip);
 293        }
 294        str += sprintf(str, "\n");
 295        return str - buf;
 296}
 297
 298static ssize_t sprintf_string(char *str, int len, char *buf)
 299{
 300        return sprintf(str, "%.*s\n", len, buf);
 301}
 302
 303/*
 304 * Helper function to verify the IBFT header.
 305 */
 306static int ibft_verify_hdr(char *t, struct ibft_hdr *hdr, int id, int length)
 307{
 308        if (hdr->id != id) {
 309                printk(KERN_ERR "iBFT error: We expected the " \
 310                                "field header.id to have %d but " \
 311                                "found %d instead!\n", id, hdr->id);
 312                return -ENODEV;
 313        }
 314        if (hdr->length != length) {
 315                printk(KERN_ERR "iBFT error: We expected the " \
 316                                "field header.length to have %d but " \
 317                                "found %d instead!\n", length, hdr->length);
 318                return -ENODEV;
 319        }
 320
 321        return 0;
 322}
 323
 324static void ibft_release(struct kobject *kobj)
 325{
 326        struct ibft_kobject *ibft =
 327                container_of(kobj, struct ibft_kobject, kobj);
 328        kfree(ibft);
 329}
 330
 331/*
 332 *  Routines for parsing the iBFT data to be human readable.
 333 */
 334static ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry,
 335                                        struct ibft_attribute *attr,
 336                                        char *buf)
 337{
 338        struct ibft_initiator *initiator = entry->initiator;
 339        void *ibft_loc = entry->header;
 340        char *str = buf;
 341
 342        if (!initiator)
 343                return 0;
 344
 345        switch (attr->type) {
 346        case ibft_init_index:
 347                str += sprintf(str, "%d\n", initiator->hdr.index);
 348                break;
 349        case ibft_init_flags:
 350                str += sprintf(str, "%d\n", initiator->hdr.flags);
 351                break;
 352        case ibft_init_isns_server:
 353                str += sprintf_ipaddr(str, initiator->isns_server);
 354                break;
 355        case ibft_init_slp_server:
 356                str += sprintf_ipaddr(str, initiator->slp_server);
 357                break;
 358        case ibft_init_pri_radius_server:
 359                str += sprintf_ipaddr(str, initiator->pri_radius_server);
 360                break;
 361        case ibft_init_sec_radius_server:
 362                str += sprintf_ipaddr(str, initiator->sec_radius_server);
 363                break;
 364        case ibft_init_initiator_name:
 365                str += sprintf_string(str, initiator->initiator_name_len,
 366                                      (char *)ibft_loc +
 367                                      initiator->initiator_name_off);
 368                break;
 369        default:
 370                break;
 371        }
 372
 373        return str - buf;
 374}
 375
 376static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry,
 377                                  struct ibft_attribute *attr,
 378                                  char *buf)
 379{
 380        struct ibft_nic *nic = entry->nic;
 381        void *ibft_loc = entry->header;
 382        char *str = buf;
 383        char *mac;
 384        int val;
 385
 386        if (!nic)
 387                return 0;
 388
 389        switch (attr->type) {
 390        case ibft_eth_index:
 391                str += sprintf(str, "%d\n", nic->hdr.index);
 392                break;
 393        case ibft_eth_flags:
 394                str += sprintf(str, "%d\n", nic->hdr.flags);
 395                break;
 396        case ibft_eth_ip_addr:
 397                str += sprintf_ipaddr(str, nic->ip_addr);
 398                break;
 399        case ibft_eth_subnet_mask:
 400                val = ~((1 << (32-nic->subnet_mask_prefix))-1);
 401                str += sprintf(str, NIPQUAD_FMT,
 402                               (u8)(val >> 24), (u8)(val >> 16),
 403                               (u8)(val >> 8), (u8)(val));
 404                break;
 405        case ibft_eth_origin:
 406                str += sprintf(str, "%d\n", nic->origin);
 407                break;
 408        case ibft_eth_gateway:
 409                str += sprintf_ipaddr(str, nic->gateway);
 410                break;
 411        case ibft_eth_primary_dns:
 412                str += sprintf_ipaddr(str, nic->primary_dns);
 413                break;
 414        case ibft_eth_secondary_dns:
 415                str += sprintf_ipaddr(str, nic->secondary_dns);
 416                break;
 417        case ibft_eth_dhcp:
 418                str += sprintf_ipaddr(str, nic->dhcp);
 419                break;
 420        case ibft_eth_vlan:
 421                str += sprintf(str, "%d\n", nic->vlan);
 422                break;
 423        case ibft_eth_mac:
 424                mac = nic->mac;
 425                str += sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x\n",
 426                               (u8)mac[0], (u8)mac[1], (u8)mac[2],
 427                               (u8)mac[3], (u8)mac[4], (u8)mac[5]);
 428                break;
 429        case ibft_eth_hostname:
 430                str += sprintf_string(str, nic->hostname_len,
 431                                      (char *)ibft_loc + nic->hostname_off);
 432                break;
 433        default:
 434                break;
 435        }
 436
 437        return str - buf;
 438};
 439
 440static ssize_t ibft_attr_show_target(struct ibft_kobject *entry,
 441                                     struct ibft_attribute *attr,
 442                                     char *buf)
 443{
 444        struct ibft_tgt *tgt = entry->tgt;
 445        void *ibft_loc = entry->header;
 446        char *str = buf;
 447        int i;
 448
 449        if (!tgt)
 450                return 0;
 451
 452        switch (attr->type) {
 453        case ibft_tgt_index:
 454                str += sprintf(str, "%d\n", tgt->hdr.index);
 455                break;
 456        case ibft_tgt_flags:
 457                str += sprintf(str, "%d\n", tgt->hdr.flags);
 458                break;
 459        case ibft_tgt_ip_addr:
 460                str += sprintf_ipaddr(str, tgt->ip_addr);
 461                break;
 462        case ibft_tgt_port:
 463                str += sprintf(str, "%d\n", tgt->port);
 464                break;
 465        case ibft_tgt_lun:
 466                for (i = 0; i < 8; i++)
 467                        str += sprintf(str, "%x", (u8)tgt->lun[i]);
 468                str += sprintf(str, "\n");
 469                break;
 470        case ibft_tgt_nic_assoc:
 471                str += sprintf(str, "%d\n", tgt->nic_assoc);
 472                break;
 473        case ibft_tgt_chap_type:
 474                str += sprintf(str, "%d\n", tgt->chap_type);
 475                break;
 476        case ibft_tgt_name:
 477                str += sprintf_string(str, tgt->tgt_name_len,
 478                                      (char *)ibft_loc + tgt->tgt_name_off);
 479                break;
 480        case ibft_tgt_chap_name:
 481                str += sprintf_string(str, tgt->chap_name_len,
 482                                      (char *)ibft_loc + tgt->chap_name_off);
 483                break;
 484        case ibft_tgt_chap_secret:
 485                str += sprintf_string(str, tgt->chap_secret_len,
 486                                      (char *)ibft_loc + tgt->chap_secret_off);
 487                break;
 488        case ibft_tgt_rev_chap_name:
 489                str += sprintf_string(str, tgt->rev_chap_name_len,
 490                                      (char *)ibft_loc +
 491                                      tgt->rev_chap_name_off);
 492                break;
 493        case ibft_tgt_rev_chap_secret:
 494                str += sprintf_string(str, tgt->rev_chap_secret_len,
 495                                      (char *)ibft_loc +
 496                                      tgt->rev_chap_secret_off);
 497                break;
 498        default:
 499                break;
 500        }
 501
 502        return str - buf;
 503}
 504
 505/*
 506 * The routine called for all sysfs attributes.
 507 */
 508static ssize_t ibft_show_attribute(struct kobject *kobj,
 509                                    struct attribute *attr,
 510                                    char *buf)
 511{
 512        struct ibft_kobject *dev =
 513                container_of(kobj, struct ibft_kobject, kobj);
 514        struct ibft_attribute *ibft_attr =
 515                container_of(attr, struct ibft_attribute, attr);
 516        ssize_t ret = -EIO;
 517        char *str = buf;
 518
 519        if (!capable(CAP_SYS_ADMIN))
 520                return -EACCES;
 521
 522        if (ibft_attr->show)
 523                ret = ibft_attr->show(dev, ibft_attr, str);
 524
 525        return ret;
 526}
 527
 528static struct sysfs_ops ibft_attr_ops = {
 529        .show = ibft_show_attribute,
 530};
 531
 532static struct kobj_type ibft_ktype = {
 533        .release = ibft_release,
 534        .sysfs_ops = &ibft_attr_ops,
 535};
 536
 537static struct kset *ibft_kset;
 538
 539static int __init ibft_check_device(void)
 540{
 541        int len;
 542        u8 *pos;
 543        u8 csum = 0;
 544
 545        len = ibft_addr->length;
 546
 547        /* Sanity checking of iBFT. */
 548        if (ibft_addr->revision != 1) {
 549                printk(KERN_ERR "iBFT module supports only revision 1, " \
 550                                "while this is %d.\n", ibft_addr->revision);
 551                return -ENOENT;
 552        }
 553        for (pos = (u8 *)ibft_addr; pos < (u8 *)ibft_addr + len; pos++)
 554                csum += *pos;
 555
 556        if (csum) {
 557                printk(KERN_ERR "iBFT has incorrect checksum (0x%x)!\n", csum);
 558                return -ENOENT;
 559        }
 560
 561        return 0;
 562}
 563
 564/*
 565 * Helper function for ibft_register_kobjects.
 566 */
 567static int __init ibft_create_kobject(struct ibft_table_header *header,
 568                                       struct ibft_hdr *hdr,
 569                                       struct list_head *list)
 570{
 571        struct ibft_kobject *ibft_kobj = NULL;
 572        struct ibft_nic *nic = (struct ibft_nic *)hdr;
 573        struct pci_dev *pci_dev;
 574        int rc = 0;
 575
 576        ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL);
 577        if (!ibft_kobj)
 578                return -ENOMEM;
 579
 580        ibft_kobj->header = header;
 581        ibft_kobj->hdr = hdr;
 582
 583        switch (hdr->id) {
 584        case id_initiator:
 585                rc = ibft_verify_hdr("initiator", hdr, id_initiator,
 586                                     sizeof(*ibft_kobj->initiator));
 587                break;
 588        case id_nic:
 589                rc = ibft_verify_hdr("ethernet", hdr, id_nic,
 590                                     sizeof(*ibft_kobj->nic));
 591                break;
 592        case id_target:
 593                rc = ibft_verify_hdr("target", hdr, id_target,
 594                                     sizeof(*ibft_kobj->tgt));
 595                break;
 596        case id_reserved:
 597        case id_control:
 598        case id_extensions:
 599                /* Fields which we don't support. Ignore them */
 600                rc = 1;
 601                break;
 602        default:
 603                printk(KERN_ERR "iBFT has unknown structure type (%d). " \
 604                                "Report this bug to %.6s!\n", hdr->id,
 605                                header->oem_id);
 606                rc = 1;
 607                break;
 608        }
 609
 610        if (rc) {
 611                /* Skip adding this kobject, but exit with non-fatal error. */
 612                kfree(ibft_kobj);
 613                goto out_invalid_struct;
 614        }
 615
 616        ibft_kobj->kobj.kset = ibft_kset;
 617
 618        rc = kobject_init_and_add(&ibft_kobj->kobj, &ibft_ktype,
 619                                  NULL, ibft_id_names[hdr->id], hdr->index);
 620
 621        if (rc) {
 622                kfree(ibft_kobj);
 623                goto out;
 624        }
 625
 626        kobject_uevent(&ibft_kobj->kobj, KOBJ_ADD);
 627
 628        if (hdr->id == id_nic) {
 629                /*
 630                * We don't search for the device in other domains than
 631                * zero. This is because on x86 platforms the BIOS
 632                * executes only devices which are in domain 0. Furthermore, the
 633                * iBFT spec doesn't have a domain id field :-(
 634                */
 635                pci_dev = pci_get_bus_and_slot((nic->pci_bdf & 0xff00) >> 8,
 636                                               (nic->pci_bdf & 0xff));
 637                if (pci_dev) {
 638                        rc = sysfs_create_link(&ibft_kobj->kobj,
 639                                               &pci_dev->dev.kobj, "device");
 640                        pci_dev_put(pci_dev);
 641                }
 642        }
 643
 644        /* Nothing broke so lets add it to the list. */
 645        list_add_tail(&ibft_kobj->node, list);
 646out:
 647        return rc;
 648out_invalid_struct:
 649        /* Unsupported structs are skipped. */
 650        return 0;
 651}
 652
 653/*
 654 * Scan the IBFT table structure for the NIC and Target fields. When
 655 * found add them on the passed-in list. We do not support the other
 656 * fields at this point, so they are skipped.
 657 */
 658static int __init ibft_register_kobjects(struct ibft_table_header *header,
 659                                          struct list_head *list)
 660{
 661        struct ibft_control *control = NULL;
 662        void *ptr, *end;
 663        int rc = 0;
 664        u16 offset;
 665        u16 eot_offset;
 666
 667        control = (void *)header + sizeof(*header);
 668        end = (void *)control + control->hdr.length;
 669        eot_offset = (void *)header + header->length - (void *)control;
 670        rc = ibft_verify_hdr("control", (struct ibft_hdr *)control, id_control,
 671                             sizeof(*control));
 672
 673        /* iBFT table safety checking */
 674        rc |= ((control->hdr.index) ? -ENODEV : 0);
 675        if (rc) {
 676                printk(KERN_ERR "iBFT error: Control header is invalid!\n");
 677                return rc;
 678        }
 679        for (ptr = &control->initiator_off; ptr < end; ptr += sizeof(u16)) {
 680                offset = *(u16 *)ptr;
 681                if (offset && offset < header->length && offset < eot_offset) {
 682                        rc = ibft_create_kobject(header,
 683                                                 (void *)header + offset,
 684                                                 list);
 685                        if (rc)
 686                                break;
 687                }
 688        }
 689
 690        return rc;
 691}
 692
 693static void ibft_unregister(struct list_head *attr_list,
 694                             struct list_head *kobj_list)
 695{
 696        struct ibft_kobject *data = NULL, *n;
 697        struct ibft_attribute *attr = NULL, *m;
 698
 699        list_for_each_entry_safe(attr, m, attr_list, node) {
 700                sysfs_remove_file(attr->kobj, &attr->attr);
 701                list_del(&attr->node);
 702                kfree(attr);
 703        };
 704        list_del_init(attr_list);
 705
 706        list_for_each_entry_safe(data, n, kobj_list, node) {
 707                list_del(&data->node);
 708                if (data->hdr->id == id_nic)
 709                        sysfs_remove_link(&data->kobj, "device");
 710                kobject_put(&data->kobj);
 711        };
 712        list_del_init(kobj_list);
 713}
 714
 715static int __init ibft_create_attribute(struct ibft_kobject *kobj_data,
 716                                         int type,
 717                                         const char *name,
 718                                         ssize_t (*show)(struct ibft_kobject *,
 719                                                         struct ibft_attribute*,
 720                                                         char *buf),
 721                                         struct list_head *list)
 722{
 723        struct ibft_attribute *attr = NULL;
 724        struct ibft_hdr *hdr = kobj_data->hdr;
 725
 726        attr = kmalloc(sizeof(*attr), GFP_KERNEL);
 727        if (!attr)
 728                return -ENOMEM;
 729
 730        attr->attr.name = name;
 731        attr->attr.mode = S_IRUSR;
 732
 733        attr->hdr = hdr;
 734        attr->show = show;
 735        attr->kobj = &kobj_data->kobj;
 736        attr->type = type;
 737
 738        list_add_tail(&attr->node, list);
 739
 740        return 0;
 741}
 742
 743/*
 744 * Helper routiners to check to determine if the entry is valid
 745 * in the proper iBFT structure.
 746 */
 747static int __init ibft_check_nic_for(struct ibft_nic *nic, int entry)
 748{
 749        int rc = 0;
 750
 751        switch (entry) {
 752        case ibft_eth_index:
 753        case ibft_eth_flags:
 754                rc = 1;
 755                break;
 756        case ibft_eth_ip_addr:
 757                if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr)))
 758                        rc = 1;
 759                break;
 760        case ibft_eth_subnet_mask:
 761                if (nic->subnet_mask_prefix)
 762                        rc = 1;
 763                break;
 764        case ibft_eth_origin:
 765                rc = 1;
 766                break;
 767        case ibft_eth_gateway:
 768                if (memcmp(nic->gateway, nulls, sizeof(nic->gateway)))
 769                        rc = 1;
 770                break;
 771        case ibft_eth_primary_dns:
 772                if (memcmp(nic->primary_dns, nulls,
 773                           sizeof(nic->primary_dns)))
 774                        rc = 1;
 775                break;
 776        case ibft_eth_secondary_dns:
 777                if (memcmp(nic->secondary_dns, nulls,
 778                           sizeof(nic->secondary_dns)))
 779                        rc = 1;
 780                break;
 781        case ibft_eth_dhcp:
 782                if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
 783                        rc = 1;
 784                break;
 785        case ibft_eth_vlan:
 786        case ibft_eth_mac:
 787                rc = 1;
 788                break;
 789        case ibft_eth_hostname:
 790                if (nic->hostname_off)
 791                        rc = 1;
 792                break;
 793        default:
 794                break;
 795        }
 796
 797        return rc;
 798}
 799
 800static int __init ibft_check_tgt_for(struct ibft_tgt *tgt, int entry)
 801{
 802        int rc = 0;
 803
 804        switch (entry) {
 805        case ibft_tgt_index:
 806        case ibft_tgt_flags:
 807        case ibft_tgt_ip_addr:
 808        case ibft_tgt_port:
 809        case ibft_tgt_lun:
 810        case ibft_tgt_nic_assoc:
 811        case ibft_tgt_chap_type:
 812                rc = 1;
 813        case ibft_tgt_name:
 814                if (tgt->tgt_name_len)
 815                        rc = 1;
 816                break;
 817        case ibft_tgt_chap_name:
 818        case ibft_tgt_chap_secret:
 819                if (tgt->chap_name_len)
 820                        rc = 1;
 821                break;
 822        case ibft_tgt_rev_chap_name:
 823        case ibft_tgt_rev_chap_secret:
 824                if (tgt->rev_chap_name_len)
 825                        rc = 1;
 826                break;
 827        default:
 828                break;
 829        }
 830
 831        return rc;
 832}
 833
 834static int __init ibft_check_initiator_for(struct ibft_initiator *init,
 835                                            int entry)
 836{
 837        int rc = 0;
 838
 839        switch (entry) {
 840        case ibft_init_index:
 841        case ibft_init_flags:
 842                rc = 1;
 843                break;
 844        case ibft_init_isns_server:
 845                if (memcmp(init->isns_server, nulls,
 846                           sizeof(init->isns_server)))
 847                        rc = 1;
 848                break;
 849        case ibft_init_slp_server:
 850                if (memcmp(init->slp_server, nulls,
 851                           sizeof(init->slp_server)))
 852                        rc = 1;
 853                break;
 854        case ibft_init_pri_radius_server:
 855                if (memcmp(init->pri_radius_server, nulls,
 856                           sizeof(init->pri_radius_server)))
 857                        rc = 1;
 858                break;
 859        case ibft_init_sec_radius_server:
 860                if (memcmp(init->sec_radius_server, nulls,
 861                           sizeof(init->sec_radius_server)))
 862                        rc = 1;
 863                break;
 864        case ibft_init_initiator_name:
 865                if (init->initiator_name_len)
 866                        rc = 1;
 867                break;
 868        default:
 869                break;
 870        }
 871
 872        return rc;
 873}
 874
 875/*
 876 *  Register the attributes for all of the kobjects.
 877 */
 878static int __init ibft_register_attributes(struct list_head *kobject_list,
 879                                            struct list_head *attr_list)
 880{
 881        int rc = 0, i = 0;
 882        struct ibft_kobject *data = NULL;
 883        struct ibft_attribute *attr = NULL, *m;
 884
 885        list_for_each_entry(data, kobject_list, node) {
 886                switch (data->hdr->id) {
 887                case id_nic:
 888                        for (i = 0; i < ibft_eth_end_marker && !rc; i++)
 889                                if (ibft_check_nic_for(data->nic, i))
 890                                        rc = ibft_create_attribute(data, i,
 891                                                ibft_eth_properties[i],
 892                                                ibft_attr_show_nic, attr_list);
 893                        break;
 894                case id_target:
 895                        for (i = 0; i < ibft_tgt_end_marker && !rc; i++)
 896                                if (ibft_check_tgt_for(data->tgt, i))
 897                                        rc = ibft_create_attribute(data, i,
 898                                                ibft_tgt_properties[i],
 899                                                ibft_attr_show_target,
 900                                                attr_list);
 901                        break;
 902                case id_initiator:
 903                        for (i = 0; i < ibft_init_end_marker && !rc; i++)
 904                                if (ibft_check_initiator_for(
 905                                        data->initiator, i))
 906                                        rc = ibft_create_attribute(data, i,
 907                                                ibft_initiator_properties[i],
 908                                                ibft_attr_show_initiator,
 909                                                attr_list);
 910                        break;
 911                default:
 912                        break;
 913                }
 914                if (rc)
 915                        break;
 916        }
 917        list_for_each_entry_safe(attr, m, attr_list, node) {
 918                rc = sysfs_create_file(attr->kobj, &attr->attr);
 919                if (rc) {
 920                        list_del(&attr->node);
 921                        kfree(attr);
 922                        break;
 923                }
 924        }
 925
 926        return rc;
 927}
 928
 929/*
 930 * ibft_init() - creates sysfs tree entries for the iBFT data.
 931 */
 932static int __init ibft_init(void)
 933{
 934        int rc = 0;
 935
 936        ibft_kset = kset_create_and_add("ibft", NULL, firmware_kobj);
 937        if (!ibft_kset)
 938                return -ENOMEM;
 939
 940        if (ibft_addr) {
 941                printk(KERN_INFO "iBFT detected at 0x%llx.\n",
 942                       (u64)virt_to_phys((void *)ibft_addr));
 943
 944                rc = ibft_check_device();
 945                if (rc)
 946                        goto out_firmware_unregister;
 947
 948                /* Scan the IBFT for data and register the kobjects. */
 949                rc = ibft_register_kobjects(ibft_addr, &ibft_kobject_list);
 950                if (rc)
 951                        goto out_free;
 952
 953                /* Register the attributes */
 954                rc = ibft_register_attributes(&ibft_kobject_list,
 955                                              &ibft_attr_list);
 956                if (rc)
 957                        goto out_free;
 958        } else
 959                printk(KERN_INFO "No iBFT detected.\n");
 960
 961        return 0;
 962
 963out_free:
 964        ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
 965out_firmware_unregister:
 966        kset_unregister(ibft_kset);
 967        return rc;
 968}
 969
 970static void __exit ibft_exit(void)
 971{
 972        ibft_unregister(&ibft_attr_list, &ibft_kobject_list);
 973        kset_unregister(ibft_kset);
 974}
 975
 976module_init(ibft_init);
 977module_exit(ibft_exit);
 978