Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751980AbXLEDNd (ORCPT ); Tue, 4 Dec 2007 22:13:33 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751004AbXLEDN0 (ORCPT ); Tue, 4 Dec 2007 22:13:26 -0500 Received: from bebe.enoyolf.org ([24.173.215.190]:55734 "EHLO bebe.enoyolf.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750915AbXLEDNY (ORCPT ); Tue, 4 Dec 2007 22:13:24 -0500 X-Mailer: exmh enoyolf.org version 2.7.2.1 01/17/2005 with nmh-1.1 In-reply-to: <20071205004418.GH6736@andromeda.dapyr.net> References: <20071128233422.GD6736@andromeda.dapyr.net> <20071205004418.GH6736@andromeda.dapyr.net> To: darnok@68k.org cc: linux-kernel@vger.kernel.org, pjones@redhat.com, konradr@redhat.com, konradr@linux.vnet.ibm.com, greg@kroah.com, randy.dunlap@oracle.com, hpa@zytor.com, lenb@kernel.org, mike.anderson@us.ibm.com, dwm@austin.ibm.com, Arjan van de Ven Subject: Re: [REPOST PATCH] Add iSCSI IBFT support (v0.4) Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Date: Tue, 04 Dec 2007 21:12:11 -0600 Message-ID: <22081.1196824331@bebe.enoyolf.org> From: Doug Maxey Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 33515 Lines: 1238 Overall, looks nice. Good work. comments inline below... On Tue, 04 Dec 2007 20:44:19 -0400, darnok@68k.org wrote: > On Wed, Nov 28, 2007 at 07:34:22PM -0400, Konrad Rzeszutek wrote: > > > > This patch adds /sysfs/firmware/ibft/[initiator|targetX|ethernetX] > > directories along with text properties which export the the iSCSI Boot > > Firmware Table (iBFT) structure. > > > > What is iSCSI Boot Firmware Table? It is a mechanism for the iSCSI > > tools to extract from the machine NICs the iSCSI connection information > > so that they can automagically mount the iSCSI share/target. Currently > > the iSCSI information is hard-coded in the initrd. > > > > For full details of the IBFT structure please take a look at: > > ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf > > > > > > Signed-off-by: Konrad Rzeszutek > > Here is an updated version which provides an attribute symlink (in the > ethernetX directory) called 'device' to the NIC device, instead of > exporting a 'pci_bdf' file which had the "bus:slot:func" values. > > Review would be much appreciated. > > Signed-off-by: Konrad Rzeszutek > > diff --git a/Documentation/ABI/testing/sysfs-ibft b/Documentation/ABI/testing/sysfs-ibft > new file mode 100644 > index 0000000..941ad9f > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-ibft > @@ -0,0 +1,31 @@ > +What: /sys/firmware/ibft/initiator > +Date: November 2007 > +Contact: Konrad Rzeszutek > +Description: The /sys/firmware/ibft/initiator directory will contain > + files that expose the iSCSI Boot Firmware Table initiator data. > + Usually this contains the Initiator name. > + > +What: /sys/firmware/ibft/targetX > +Date: November 2007 > +Contact: Konrad Rzeszutek > +Description: The /sys/firmware/ibft/targetX directory will contain > + files that expose the iSCSI Boot Firmware Table target data. > + Usually this contains the target's IP address, boot LUN, > + target name, and what NIC it is associated with. It can also > + contain the CHAP name (and password), the reverse CHAP > + name (and password) > + > +What: /sys/firmware/ibft/ethernetX > +Date: November 2007 > +Contact: Konrad Rzeszutek > +Description: The /sys/firmware/ibft/ethernetX directory will contain > + files that expose the iSCSI Boot Firmware Table NIC data. > + This can this can the IP address, MAC, and gateway of the NIC. > + > +What: /sys/firmware/ibft/extensionX > +Date: November 2007 > +Contact: Konrad Rzeszutek > +Description: The /sys/firmware/ibft/extensionX directory will contain > + files that expose the iSCSI Boot Firmware Table extension data. > + The extended data structure is not supported so there would be > + no files in this directory. > diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c > index e1e18c3..7c36a28 100644 > --- a/arch/x86/kernel/setup_32.c > +++ b/arch/x86/kernel/setup_32.c > @@ -39,6 +39,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -148,6 +149,20 @@ static inline void copy_edd(void) > } > #endif > > +#if defined(CONFIG_ISCSI_IBFT_FIND) > +static void __init reserve_ibft_region(void) > +{ > + unsigned int ibft_len; > + > + ibft_len = find_ibft(); > + if (ibft_len) > + reserve_bootmem((unsigned int)ibft_phys, > + PAGE_ALIGN(ibft_len)); > +} > +#else > +static void __init reserve_ibft_region(void) { } > +#endif > + > int __initdata user_defined_memmap = 0; > > /* > @@ -496,6 +511,7 @@ void __init setup_bootmem_allocator(void) > } > #endif > reserve_crashkernel(); > + reserve_ibft_region(); > } > > /* > diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c > index 30d94d1..d5b73c6 100644 > --- a/arch/x86/kernel/setup_64.c > +++ b/arch/x86/kernel/setup_64.c > @@ -33,6 +33,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -198,6 +199,20 @@ static inline void copy_edd(void) > } > #endif > > +#if defined(CONFIG_ISCSI_IBFT_FIND) > +static void __init reserve_ibft_region(void) > +{ > + unsigned int ibft_len; > + > + ibft_len = find_ibft(); > + if (ibft_len) > + reserve_bootmem_generic((unsigned long)ibft_phys, > + PAGE_ALIGN(ibft_len)); > +} > +#else > +static void __init reserve_ibft_region(void) { } > +#endif > + > #ifdef CONFIG_KEXEC > static void __init reserve_crashkernel(void) > { > @@ -403,6 +418,7 @@ void __init setup_arch(char **cmdline_p) > } > #endif > reserve_crashkernel(); > + reserve_ibft_region(); > paging_init(); > > #ifdef CONFIG_PCI > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > index 05f02a3..e2f7208 100644 > --- a/drivers/firmware/Kconfig > +++ b/drivers/firmware/Kconfig > @@ -93,4 +93,23 @@ config DMIID > information from userspace through /sys/class/dmi/id/ or if you want > DMI-based module auto-loading. > > +config ISCSI_IBFT_FIND > + bool "iSCSI Boot Firmware Table Attributes" > + default n > + help > + This option enables the kernel to find the region of memory > + in which the ISCSI Boot Firmware Table (iBFT) resides. This > + is necessary for iSCSI Boot Firmware Table Attributes module to work > + properly. > + > +config ISCSI_IBFT > + tristate "iSCSI Boot Firmware Table Attributes module" > + depends on ISCSI_IBFT_FIND > + default n > + help > + This option enables support for detection and exposing of iSCSI > + Boot Firmware Table (iBFT) via sysfs to userspace. If you wish to > + detect iSCSI boot parameters dynamically during system boot, say Y. > + Otherwise, say N. > + > endmenu > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > index 8d4ebc8..4c91471 100644 > --- a/drivers/firmware/Makefile > +++ b/drivers/firmware/Makefile > @@ -8,3 +8,5 @@ obj-$(CONFIG_EFI_PCDP) += pcdp.o > obj-$(CONFIG_DELL_RBU) += dell_rbu.o > obj-$(CONFIG_DCDBAS) += dcdbas.o > obj-$(CONFIG_DMIID) += dmi-id.o > +obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o > +obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o > diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c > new file mode 100644 > index 0000000..d668abc > --- /dev/null > +++ b/drivers/firmware/iscsi_ibft.c > @@ -0,0 +1,781 @@ > +/* > + * Copyright 2007 Red Hat, Inc. > + * by Peter Jones > + * Copyright 2007 IBM, Inc. > + * by Konrad Rzeszutek > + * > + * This code exposes the iSCSI Boot Format Table to userland via sysfs. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License v2.0 as published by > + * the Free Software Foundation > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include Is the current include from open-iscsi being duplicated? If not, why not consolidate in one file? > + > +#define IBFT_ISCSI_VERSION "0.4.1" > +#define IBFT_ISCSI_DATE "2007-Dec-04" > + > +MODULE_AUTHOR("Peter Jones and \ > +Konrad Rzeszutek "); > +MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information"); > +MODULE_LICENSE("GPL"); > +MODULE_VERSION(IBFT_ISCSI_VERSION); > + > +/* > + * The kobject different types and its names. > + * > +*/ > +enum ibft_id { > + id_reserved = 0, /* Should never show up */ > + id_control = 1, /* Ditto */ > + id_initiator = 2, > + id_nic = 3, > + id_target = 4, > + id_extensions, /* We don't support. */ > +}; > + > +static const char *ibft_id_names[] = > + {"reserved", "control", "initiator", "ethernet%d", > + "target%d", "extension%d"}; closing null arg. > + > +/* > + * The text attributes names for each of the kobjects. > +*/ > +enum ibft_eth_properties_enum { > + ibft_eth_index, > + ibft_eth_flags, > + ibft_eth_ip_addr, > + ibft_eth_subnet_mask, > + ibft_eth_origin, > + ibft_eth_gateway, > + ibft_eth_primary_dns, > + ibft_eth_secondary_dns, > + ibft_eth_dhcp, > + ibft_eth_vlan, > + ibft_eth_mac, > + /* ibft_eth_pci_bdf - this is replaced by link to the device itself. */ > + ibft_eth_hostname}; I noticed ibft_eth_hostname gets used as the last marker below. Wouldn't it be better to have a ibft_eth_count or some such to really be the last? That way if the elements so change, it still marks the last element. > + > +static const char *ibft_eth_properties[] = > + {"index", "flags", "ip-addr", "subnet-mask", "origin", "gateway", > + "primary-dns", "secondary-dns", "dhcp", "vlan", "mac", "hostname"}; > + null last entry? > +enum ibft_tgt_properties_enum { > + ibft_tgt_index, > + ibft_tgt_flags, > + ibft_tgt_ip_addr, > + ibft_tgt_port, > + ibft_tgt_lun, > + ibft_tgt_chap_type, > + ibft_tgt_nic_assoc, > + ibft_tgt_name, > + ibft_tgt_chap_name, > + ibft_tgt_chap_secret, > + ibft_tgt_rev_chap_name, > + ibft_tgt_rev_chap_secret}; > + > +static const char *ibft_tgt_properties[] = > + {"index", "flags", "ip-addr", "port", "lun", "chap-type", "nic-assoc", > + "target-name", "chap-name", "chap-secret", "rev-chap-name", > + "rev-chap-name-secret"}; ditto > + > +enum ibft_initiator_properties_enum { > + ibft_init_index, > + ibft_init_flags, > + ibft_init_isns_server, > + ibft_init_slp_server, > + ibft_init_pri_radius_server, > + ibft_init_sec_radius_server, > + ibft_init_initiator_name}; > + > +static const char *ibft_initiator_properties[] = > + {"index", "flags", "isns-server", "slp-server", "pri-radius-server", > + "sec-radius-server", "initiator-name"}; > + ditto > +static LIST_HEAD(ibft_attr_list); > +static LIST_HEAD(ibft_kobject_list); > + > +static const char nulls[16]; > +static struct ibft_table_header *ibft_device; > + > +/* > + * Helper functions to parse data properly. > + */ > +static ssize_t sprintf_ipaddr(char *buf, u8 *ip) > +{ > + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && > + ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0 && > + ip[8] == 0 && ip[9] == 0 && ip[10] == 0xff && ip[11] == 0xff) { > + /* > + * IPV4 > + */ > + return sprintf(buf, "%d.%d.%d.%d\n", ip[12], > + ip[13], ip[14], ip[15]); > + } else { > + return 0; > + } redundant braces on the else > +} > + > +static ssize_t sprintf_string(char *str, int len, char *buf) > +{ > + return sprintf(str, "%.*s\n", len, buf); > +} > + > +/* > + * Helper function to verify the IBFT header. > + */ > +static int ibft_verify_hdr(struct ibft_hdr *hdr, int id, int length) > +{ > +#define IBFT_VERIFY_HDR_FIELD(val, name) \ > + if (hdr->val != val) { \ > + printk(KERN_ERR \ > + "error, in IBFT structure (%s) expected %d but" \ > + " found %d\n", \ > + name, val, hdr->val); \ > + return -ENODEV; \ > + } > + IBFT_VERIFY_HDR_FIELD(id, "ID"); > + IBFT_VERIFY_HDR_FIELD(length, "Length"); > +#undef IBFT_VERIFY_HDR_FIELD > + return 0; > +} > + > +static void ibft_release(struct kobject *kobj) > +{ > + struct ibft_kobject *ibft = > + container_of(kobj, struct ibft_kobject, kobj); > + kfree(ibft); > +} > + > +/* > + * Routines for reading of the iBFT data in a human readable fashion. > + */ > +ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry, > + struct ibft_attribute *attr, > + char *buf) > +{ > + struct ibft_initiator *initiator = entry->initiator; > + void *ibft_loc = entry->header; > + char *str = buf; > + > + if (!initiator) > + return 0; > + > + switch (attr->type) { > + case ibft_init_index: > + str += sprintf(str, "%d\n", initiator->hdr.index); > + break; > + case ibft_init_flags: > + str += sprintf(str, "%d\n", initiator->hdr.flags); > + break; > + case ibft_init_isns_server: > + str += sprintf_ipaddr(str, initiator->isns_server); > + break; > + case ibft_init_slp_server: > + str += sprintf_ipaddr(str, initiator->slp_server); > + break; > + case ibft_init_pri_radius_server: > + str += sprintf_ipaddr(str, initiator->pri_radius_server); > + break; > + case ibft_init_sec_radius_server: > + str += sprintf_ipaddr(str, initiator->sec_radius_server); > + break; > + case ibft_init_initiator_name: > + str += sprintf_string(str, initiator->initiator_name_len, > + (char *)ibft_loc + initiator->initiator_name_off); > + break; > + default: > + break; > + } > + > + return str - buf; > +} > + > +ssize_t ibft_attr_show_nic(struct ibft_kobject *entry, > + struct ibft_attribute *attr, > + char *buf) > +{ > + struct ibft_nic *nic = entry->nic; > + void *ibft_loc = entry->header; > + char *str = buf; > + char *mac; > + > + if (!nic) > + return 0; > + > + switch (attr->type) { > + case ibft_eth_index: > + str += sprintf(str, "%d\n", nic->hdr.index); > + break; > + case ibft_eth_flags: > + str += sprintf(str, "%d\n", nic->hdr.flags); > + break; > + case ibft_eth_ip_addr: > + str += sprintf_ipaddr(str, nic->ip_addr); > + break; > + case ibft_eth_subnet_mask: > + str += sprintf(str, "%d\n", nic->subnet_mask_prefix); > + break; > + case ibft_eth_origin: > + str += sprintf(str, "%d\n", nic->origin); > + break; > + case ibft_eth_gateway: > + str += sprintf_ipaddr(str, nic->gateway); > + break; > + case ibft_eth_primary_dns: > + str += sprintf_ipaddr(str, nic->primary_dns); > + break; > + case ibft_eth_secondary_dns: > + str += sprintf_ipaddr(str, nic->secondary_dns); > + break; > + case ibft_eth_dhcp: > + str += sprintf_ipaddr(str, nic->dhcp); > + break; > + case ibft_eth_vlan: > + str += sprintf(str, "%d\n", nic->vlan); > + break; > + case ibft_eth_mac: > + mac = nic->mac; > + str += sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x\n", > + (u8)mac[0], (u8)mac[1], (u8)mac[2], > + (u8)mac[3], (u8)mac[4], (u8)mac[5]); > + break; > + case ibft_eth_hostname: > + str += sprintf_string(str, nic->hostname_len, > + (char *)ibft_loc + nic->hostname_off); > + break; > + default: > + break; > + } > + > + return str - buf; > +}; > + > +ssize_t ibft_attr_show_target(struct ibft_kobject *entry, > + struct ibft_attribute *attr, > + char *buf) > +{ > + struct ibft_tgt *tgt = entry->tgt; > + void *ibft_loc = entry->header; > + char *str = buf; > + int i; > + > + if (!tgt) > + return 0; > + > + switch (attr->type) { > + case ibft_tgt_index: > + str += sprintf(str, "%d\n", tgt->hdr.index); > + break; > + case ibft_tgt_flags: > + str += sprintf(str, "%d\n", tgt->hdr.flags); > + break; > + case ibft_tgt_ip_addr: > + str += sprintf_ipaddr(str, tgt->ip_addr); > + break; > + case ibft_tgt_port: > + str += sprintf(str, "%d\n", tgt->port); > + break; > + case ibft_tgt_lun: > + for (i = 0; i < 8; i++) > + str += sprintf(str, "%x", (u8)tgt->lun[i]); > + str += sprintf(str, "\n"); > + break; > + case ibft_tgt_nic_assoc: > + str += sprintf(str, "%d\n", tgt->nic_assoc); > + break; > + case ibft_tgt_chap_type: > + str += sprintf(str, "%d\n", tgt->chap_type); > + break; > + case ibft_tgt_name: > + str += sprintf_string(str, tgt->tgt_name_len, > + (void *)ibft_loc + tgt->tgt_name_off); > + break; > + case ibft_tgt_chap_name: > + str += sprintf_string(str, tgt->chap_name_len, > + (char *)ibft_loc + tgt->chap_name_off); > + break; > + case ibft_tgt_chap_secret: > + str += sprintf_string(str, tgt->chap_secret_len, > + (char *)ibft_loc + tgt->chap_secret_off); > + break; > + case ibft_tgt_rev_chap_name: > + str += sprintf_string(str, tgt->rev_chap_name_len, > + (char *)ibft_loc + tgt->rev_chap_name_off); > + break; > + case ibft_tgt_rev_chap_secret: > + str += sprintf_string(str, tgt->rev_chap_secret_len, > + (char *)ibft_loc + tgt->rev_chap_secret_off); > + break; > + default: > + break; > + } > + > + return str - buf; > +} > + > +/* > + * The main routine which allows the user to read the IBFT data. > + */ > +static ssize_t ibft_show_attribute(struct kobject *kobj, > + struct attribute *attr, > + char *buf) > +{ > + struct ibft_kobject *dev = > + container_of(kobj, struct ibft_kobject, kobj); > + struct ibft_attribute *ibft_attr = > + container_of(attr, struct ibft_attribute, attr); > + ssize_t ret = -EIO; > + char *str = buf; > + > + if (!capable(CAP_SYS_ADMIN)) > + return -EACCES; > + > + if (ibft_attr->show) > + ret = ibft_attr->show(dev, ibft_attr, str); > + > + return ret; > +} > + > +static struct sysfs_ops ibft_attr_ops = { > + .show = ibft_show_attribute, > +}; > + > +static struct kobj_type ktype_ibft = { > + .release = ibft_release, > + .sysfs_ops = &ibft_attr_ops, > +}; > + > +static decl_subsys(ibft, &ktype_ibft, NULL); > + > +static int ibft_alloc_device(void *idev) > +{ > + int len; > + struct ibft_table_header *hdr; > + > + if (!idev) > + return -ENOENT; > + > + hdr = (struct ibft_table_header *)phys_to_virt( > + (unsigned long)ibft_phys); > + len = hdr->length; > + > + ibft_device = kzalloc(len, GFP_KERNEL); > + if (!ibft_device) > + return -ENOMEM; > + > + memcpy(ibft_device, hdr, len); > + > + return 0; > +} > + > +static void ibft_free_device(struct ibft_table_header *hdr) > +{ > + kfree(hdr); > +} > + > +/* > + * Helper function for ibft_register_kobjects. > + */ > +static int ibft_create_kobject(struct ibft_table_header *header, > + struct ibft_hdr *hdr, > + struct list_head *list) > +{ > + int rc = 0; > + struct ibft_kobject *ibft_kobj = NULL; > + > + ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL); Is this going to collide with the new kobject semantics? Subject: [RFC] New kobject/kset/ktype documentation and example code Message-ID: <20071127230252.GB10038@kroah.com> > + if (!ibft_kobj) > + return -ENOMEM; > + > + ibft_kobj->header = header; > + ibft_kobj->hdr = hdr; > + > + switch (hdr->id) { > + case id_initiator: > + rc = ibft_verify_hdr(hdr, id_initiator, > + sizeof(*ibft_kobj->initiator)); > + break; > + case id_nic: > + rc = ibft_verify_hdr(hdr, id_nic, > + sizeof(*ibft_kobj->nic)); > + break; > + case id_target: > + rc = ibft_verify_hdr(hdr, id_target, > + sizeof(*ibft_kobj->tgt)); > + break; > + default: > + /* Extension field which we don't support. Ignore it */ > + break; > + } > + > + if (!rc) { > + kobject_set_name(&ibft_kobj->kobj, > + ibft_id_names[hdr->id], > + hdr->index); > + > + kobj_set_kset_s(ibft_kobj, ibft_subsys); > + > + rc = kobject_register(&ibft_kobj->kobj); > + if (!rc && hdr->id == id_nic) { > + struct ibft_nic *nic = (struct ibft_nic *)hdr; > + struct pci_dev *pci_dev = pci_get_bus_and_slot( > + (nic->pci_bdf & 0xff00) >> 8, > + (nic->pci_bdf & 0xff)); > + if (pci_dev) { > + rc = sysfs_create_link(&ibft_kobj->kobj, > + &pci_dev->dev.kobj, "device"); > + if (rc) > + kobject_unregister(&ibft_kobj->kobj); > + pci_dev_put(pci_dev); > + } > + } > + if (!rc) > + list_add_tail(&ibft_kobj->node, list); > + > + } > + if (rc) > + kfree(ibft_kobj); > + > + return rc; > +} > + > +/* > + * Scan the IBFT table structure for the NIC and Target fields. When > + * found add them on the passed-in list. > + */ > +static int ibft_register_kobjects(struct ibft_table_header *header, > + struct list_head *list) > +{ > + struct ibft_control *control = NULL; > + void *ptr, *end; > + int rc = 0; > + u16 offset; > + > + control = (void *)header + sizeof(*header); > + end = (void *)control + control->hdr.length; > + > + for (ptr = &control->initiator_off; ptr < end; ptr += sizeof(u16)) { > + offset = *(u16 *)ptr; > + if (offset && offset < header->length) { > + rc = ibft_create_kobject(header, > + (void *)header + offset, > + list); > + if (rc) > + break; > + } > + } > + > + return rc; > +} > + > +static void ibft_unregister_kobjects(struct list_head *list) > +{ > + struct ibft_kobject *data = NULL, *n; > + list_for_each_entry_safe(data, n, list, node) { > + list_del(&data->node); > + if (data->hdr->id == id_nic) > + sysfs_remove_link(&data->kobj, "device"); > + kobject_unregister(&data->kobj); > + }; > + list_del_init(list); > +} > + > +static int ibft_create_attribute(struct ibft_kobject *kobj_data, > + int type, > + const char *name, > + ssize_t (*show) (struct ibft_kobject *entry, > + struct ibft_attribute *attr, > + char *buf), > + struct list_head *list) > +{ > + struct ibft_attribute *attr = NULL; > + struct ibft_hdr *hdr = kobj_data->hdr; > + > + attr = kzalloc(sizeof(*attr), GFP_KERNEL); > + if (!attr) > + return -ENOMEM; > + > + attr->attr.name = name; > + attr->attr.mode = S_IRUSR; > + attr->attr.owner = THIS_MODULE; > + > + attr->hdr = hdr; > + attr->show = show; > + attr->kobj = &kobj_data->kobj; > + attr->type = type; > + > + list_add_tail(&attr->node, list); > + > + return 0; > +} > + > +static int ibft_check_nic_for(struct ibft_nic *nic, int entry) > +{ > + int rc = 0; > + > + switch (entry) { > + case ibft_eth_index: > + case ibft_eth_flags: > + rc = 1; > + break; > + case ibft_eth_ip_addr: > + if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) > + rc = 1; > + break; > + case ibft_eth_subnet_mask: > + if (!memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) > + rc = 1; > + break; > + case ibft_eth_origin: > + rc = 1; > + break; > + case ibft_eth_gateway: > + if (memcmp(nic->gateway, nulls, sizeof(nic->gateway))) > + rc = 1; > + break; > + case ibft_eth_primary_dns: > + if (memcmp(nic->primary_dns, nulls, > + sizeof(nic->primary_dns))) > + rc = 1; > + break; > + case ibft_eth_secondary_dns: > + if (memcmp(nic->secondary_dns, nulls, > + sizeof(nic->secondary_dns))) > + rc = 1; > + break; > + case ibft_eth_dhcp: > + if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) > + rc = 1; > + break; > + case ibft_eth_vlan: > + case ibft_eth_mac: > + rc = 1; > + break; > + case ibft_eth_hostname: > + if (nic->hostname_off) > + rc = 1; > + break; > + default: > + break; > + } > + > + return rc; > +} > + > +static int ibft_check_tgt_for(struct ibft_tgt *tgt, int entry) > +{ > + int rc = 0; > + > + switch (entry) { > + case ibft_tgt_index: > + case ibft_tgt_flags: > + case ibft_tgt_ip_addr: > + case ibft_tgt_port: > + case ibft_tgt_lun: > + case ibft_tgt_nic_assoc: > + case ibft_tgt_chap_type: > + rc = 1; > + case ibft_tgt_name: > + if (tgt->tgt_name_len) > + rc = 1; > + break; > + case ibft_tgt_chap_name: > + case ibft_tgt_chap_secret: > + if (tgt->chap_name_len) > + rc = 1; > + break; > + case ibft_tgt_rev_chap_name: > + case ibft_tgt_rev_chap_secret: > + if (tgt->rev_chap_name_len) > + rc = 1; > + break; > + default: > + break; > + } > + > + return rc; > +} > + > +static int ibft_check_initiator_for(struct ibft_initiator *init, int entry) > +{ > + int rc = 0; > + > + switch (entry) { > + case ibft_init_index: > + case ibft_init_flags: > + rc = 1; > + break; > + case ibft_init_isns_server: > + if (memcmp(init->isns_server, nulls, > + sizeof(init->isns_server))) > + rc = 1; > + break; > + case ibft_init_slp_server: > + if (memcmp(init->slp_server, nulls, > + sizeof(init->slp_server))) > + rc = 1; > + break; > + case ibft_init_pri_radius_server: > + if (memcmp(init->pri_radius_server, nulls, > + sizeof(init->pri_radius_server))) > + rc = 1; > + break; > + case ibft_init_sec_radius_server: > + if (memcmp(init->sec_radius_server, nulls, > + sizeof(init->sec_radius_server))) > + rc = 1; > + break; > + case ibft_init_initiator_name: > + if (init->initiator_name_len) > + rc = 1; > + break; > + default: > + break; > + } > + > + return rc; > +} > + > +/* > + * Register the attributes for all of the kobjects. > + */ > +static int ibft_register_attributes(struct list_head *kobject_list, > + struct list_head *attr_list) > +{ > + int rc = 0, i = 0; > + struct ibft_kobject *data = NULL; > + struct ibft_attribute *attr = NULL, *m; > + > + list_for_each_entry(data, kobject_list, node) { > + switch (data->hdr->id) { > + case id_nic: > + for (i = 0; i <= ibft_eth_hostname && !rc; i++) > + if (ibft_check_nic_for(data->nic, i)) > + rc = ibft_create_attribute(data, i, > + ibft_eth_properties[i], > + ibft_attr_show_nic, attr_list); > + break; > + case id_target: > + for (i = 0; i <= ibft_tgt_rev_chap_secret && !rc; i++) > + if (ibft_check_tgt_for(data->tgt, i)) > + rc = ibft_create_attribute(data, i, > + ibft_tgt_properties[i], > + ibft_attr_show_target, > + attr_list); > + break; > + case id_initiator: > + for (i = 0; i <= ibft_init_initiator_name && !rc; i++) > + if (ibft_check_initiator_for( > + data->initiator, i)) > + rc = ibft_create_attribute(data, i, > + ibft_initiator_properties[i], > + ibft_attr_show_initiator, > + attr_list); > + break; > + default: > + break; > + } > + if (rc) > + break; > + } > + list_for_each_entry_safe(attr, m, attr_list, node) { > + rc = sysfs_create_file(attr->kobj, &attr->attr); > + if (rc) { > + list_del(&attr->node); > + kfree(attr); > + break; > + } > + } > + > + return rc; > +} > + > +static void ibft_unregister_attributes(struct list_head *list) > +{ > + struct ibft_attribute *attr = NULL, *m; > + > + list_for_each_entry_safe(attr, m, list, node) { > + sysfs_remove_file(attr->kobj, &attr->attr); > + list_del(&attr->node); > + kfree(attr); > + } > + list_del_init(list); > +} > + > +/* > + * ibft_init() - creates sysfs tree entries for iBFT data. > + */ > +static int __init ibft_init(void) > +{ > + int rc = 0; > + > + if (!ibft_phys) > + find_ibft(); > + > + rc = firmware_register(&ibft_subsys); > + if (rc) > + return rc; > + > + if (ibft_phys) { > + printk(KERN_INFO "iBFT detected at 0x%lx.\n", > + (unsigned long)ibft_phys); > + > + rc = ibft_alloc_device(ibft_phys); > + if (rc) > + goto out_firmware_unregister; > + > + /* Scan the IBFT for data and register kobjects. */ > + rc = ibft_register_kobjects(ibft_device, &ibft_kobject_list); > + if (rc) > + goto out_free; > + > + /* Register the attributes */ > + rc = ibft_register_attributes(&ibft_kobject_list, > + &ibft_attr_list); > + if (rc) > + goto out_free; > + } else > + printk(KERN_INFO "No iBFT detected.\n"); > + > + return 0; > + > +out_free: > + ibft_unregister_attributes(&ibft_attr_list); > + ibft_unregister_kobjects(&ibft_kobject_list); > + ibft_free_device(ibft_device); > + ibft_device = NULL; > +out_firmware_unregister: > + firmware_unregister(&ibft_subsys); > + return rc; > +} > + > +static void __exit ibft_exit(void) > +{ > + ibft_unregister_attributes(&ibft_attr_list); > + ibft_unregister_kobjects(&ibft_kobject_list); > + ibft_free_device(ibft_device); > + ibft_device = NULL; > + firmware_unregister(&ibft_subsys); > +} > + > +module_init(ibft_init); > +module_exit(ibft_exit); > diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c > new file mode 100644 > index 0000000..c51685a > --- /dev/null > +++ b/drivers/firmware/iscsi_ibft_find.c > @@ -0,0 +1,78 @@ > +/* > + * Copyright 2007 Red Hat, Inc. > + * by Peter Jones > + * Copyright 2007 IBM, Inc. > + * by Konrad Rzeszutek > + * > + * This code finds the iSCSI Boot Format Table. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License v2.0 as published by > + * the Free Software Foundation > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > + > +/* > + * Physical location of iSCSI Boot Format Table. > + */ > +void *ibft_phys; > +EXPORT_SYMBOL(ibft_phys); > + > +#define IBFT_SIGN "iBFT" > +#define IBFT_SIGN_LEN 4 > +#define IBFT_START 0x80000 /* 512kB */ > +#define IBFT_END 0x100000 /* 1MB */ > +#define VGA_MEM 0xA0000 /* VGA buffer */ > +#define VGA_SIZE 0x20000 /* 128kB */ > + > + > +/* > + * Routine used to find the iSCSI Boot Format Table. the physical > + * location is set in the ibft_phys variable. The return value is > + * the size of IBFT. > + */ > +ssize_t find_ibft(void) > +{ > + unsigned long pos; > + > + for (pos = IBFT_START; pos < IBFT_END; pos += 16) { > + void *virt; > + /* The table can't be inside the VGA BIOS reserved space, > + * so skip that area */ > + if (pos == VGA_MEM) > + pos += VGA_SIZE; > + virt = phys_to_virt(pos); > + if (memcmp(virt, IBFT_SIGN, IBFT_SIGN_LEN) == 0) { > + unsigned long *addr = > + (unsigned long *)phys_to_virt(pos + 4); > + unsigned int len = *addr; > + /* if the length of the table extends past 1M, > + * the table cannot be valid. */ > + if (pos + len <= (IBFT_END-1)) { > + ibft_phys = (void *)pos; > + return len; > + } > + } > + } > + return 0; > +} > +EXPORT_SYMBOL(find_ibft); > diff --git a/include/linux/iscsi_ibft.h b/include/linux/iscsi_ibft.h > new file mode 100644 > index 0000000..420f621 > --- /dev/null > +++ b/include/linux/iscsi_ibft.h > @@ -0,0 +1,126 @@ > +#ifndef ISCSI_IBFT_H > +#define ISCSI_IBFT_H missing copyright or left. :) > + > +/* > + * Physical location of iSCSI Boot Format Table. > + */ > +extern void *ibft_phys; > + > +/* > + * Routine used to find the iSCSI Boot Format Table. The physical > + * location is set in the ibft_phys variable. The return value is the > + * size of IBFT. > + */ > +extern ssize_t find_ibft(void); > + > +#if defined(CONFIG_ISCSI_IBFT) || defined(CONFIG_ISCSI_IBFT_MODULE) > + > +struct ibft_table_header { > + char signature[4]; > + u32 length; > + u8 revision; > + u8 checksum; > + char oem_id[6]; > + char oem_table_id[8]; > + char reserved[24]; > +} __attribute__((__packed__)); > + > +struct ibft_hdr { > + u8 id; > + u8 version; > + u16 length; > + u8 index; > + u8 flags; > +} __attribute__((__packed__)); > + > +struct ibft_control { > + struct ibft_hdr hdr; > + u16 extensions; > + u16 initiator_off; > + u16 nic0_off; > + u16 tgt0_off; > + u16 nic1_off; > + u16 tgt1_off; > +} __attribute__((__packed__)); > + > +struct ibft_initiator { > + struct ibft_hdr hdr; > + char isns_server[16]; > + char slp_server[16]; > + char pri_radius_server[16]; > + char sec_radius_server[16]; > + u16 initiator_name_len; > + u16 initiator_name_off; > +} __attribute__((__packed__)); > + > +struct ibft_nic { > + struct ibft_hdr hdr; > + char ip_addr[16]; > + u8 subnet_mask_prefix; > + u8 origin; > + char gateway[16]; > + char primary_dns[16]; > + char secondary_dns[16]; > + char dhcp[16]; > + u16 vlan; > + char mac[6]; > + u16 pci_bdf; > + u16 hostname_len; > + u16 hostname_off; > +} __attribute__((__packed__)); > + > +struct ibft_tgt { > + struct ibft_hdr hdr; > + char ip_addr[16]; > + u16 port; > + char lun[8]; > + u8 chap_type; > + u8 nic_assoc; > + u16 tgt_name_len; > + u16 tgt_name_off; > + u16 chap_name_len; > + u16 chap_name_off; > + u16 chap_secret_len; > + u16 chap_secret_off; > + u16 rev_chap_name_len; > + u16 rev_chap_name_off; > + u16 rev_chap_secret_len; > + u16 rev_chap_secret_off; > +} __attribute__((__packed__)); > + > +/* > + * The kobject and attribute structures. > + */ > + > +struct ibft_kobject { > + struct ibft_table_header *header; > + union { > + struct ibft_initiator *initiator; > + struct ibft_nic *nic; > + struct ibft_tgt *tgt; > + struct ibft_hdr *hdr; > + }; > + struct kobject kobj; > + struct list_head node; > +}; > + > +struct ibft_attribute { > + struct attribute attr; > + ssize_t (*show) (struct ibft_kobject *entry, > + struct ibft_attribute *attr, char *buf); > + union { > + struct ibft_initiator *initiator; > + struct ibft_nic *nic; > + struct ibft_tgt *tgt; > + struct ibft_hdr *hdr; > + }; > + struct kobject *kobj; > + int type; /* The enum of the type. This can be any value off: > + ibft_eth_properties_enum, ibft_tgt_properties_enum, > + or ibft_initiator_properties_enum. */ > + struct list_head node; > +}; > + > +#endif > + > +#endif /* ISCSI_IBFT_H */ > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/