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