[PATCH] Add iSCSI iBFT support.

From: Konrad Rzeszutek
Date: Wed Sep 26 2007 - 14:53:25 EST


This patch adds a /sysfs/firmware/ibft/table binary blob which exports
the iSCSI Boot Firmware Table (iBFT) structure.

What is iSCSI Boot Firmware Table? It is a mechanism for the iSCSI
tools to extract from the machine NICs the iSCSI connection information
so that they can automagically mount the iSCSI share/target. Currently
the iSCSI information is hard-coded in the initrd.

The full details of the structure are located at:
ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf

Signed-off-by: Konrad Rzeszutek <konradr@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Peter Jones <pjones@xxxxxxxxxx>

diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index d474cd6..11d700f 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -46,7 +46,7 @@ #include <linux/kexec.h>
#include <linux/crash_dump.h>
#include <linux/dmi.h>
#include <linux/pfn.h>
-
+#include <linux/iscsi_ibft.h>
#include <video/edid.h>

#include <asm/apic.h>
@@ -150,6 +150,9 @@ static inline void copy_edd(void)
}
#endif

+void *ibft_phys;
+EXPORT_SYMBOL(ibft_phys);
+
int __initdata user_defined_memmap = 0;

/*
@@ -456,6 +459,15 @@ #ifdef CONFIG_KEXEC
reserve_bootmem(crashk_res.start,
crashk_res.end - crashk_res.start + 1);
#endif
+
+ /* Scan for an iBFT (iSCSI Boot Firmware Table) */
+ {
+ unsigned int ibft_len = find_ibft();
+ if (ibft_len)
+ /* The specs says to scan for the table between 512k to 1MB.
+ We reserve it n case it is in the e820 RAM section. */
+ reserve_bootmem(ibft_phys, PAGE_ALIGN(ibft_len));
+ }
}

/*
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
index af838f6..0d12775 100644
--- a/arch/x86_64/kernel/setup.c
+++ b/arch/x86_64/kernel/setup.c
@@ -44,6 +44,7 @@ #include <linux/cpufreq.h>
#include <linux/dmi.h>
#include <linux/dma-mapping.h>
#include <linux/ctype.h>
+#include <linux/iscsi_ibft.h>

#include <asm/mtrr.h>
#include <asm/uaccess.h>
@@ -196,6 +197,9 @@ static inline void copy_edd(void)
}
#endif

+void *ibft_phys;
+EXPORT_SYMBOL(ibft_phys);
+
#define EBDA_ADDR_POINTER 0x40E

unsigned __initdata ebda_addr;
@@ -365,6 +369,15 @@ #ifdef CONFIG_KEXEC
crashk_res.end - crashk_res.start + 1);
}
#endif
+ /* Scan for an iBFT (iSCSI Boot Firmware Table) */
+ {
+ unsigned int ibft_len = find_ibft();
+ if (ibft_len)
+ /* The specs says to scan for the table between 512k to 1MB.
+ We reserve it in case it is in the e820 RAM section. */
+ reserve_bootmem_generic((unsigned long)ibft_phys,
+ PAGE_ALIGN(ibft_len));
+ }

paging_init();

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 05f02a3..2d9f01a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -93,4 +93,14 @@ config DMIID
information from userspace through /sys/class/dmi/id/ or if you want
DMI-based module auto-loading.

+config ISCSI_IBFT
+ tristate "iSCSI Boot Firmware Table Attributes"
+ depends on X86
+ default n
+ help
+ This option enables support for detection of an iSCSI
+ Boot Firmware Table (iBFT). 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..b6319f7 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -8,3 +8,4 @@ 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) += iscsi_ibft.o
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
new file mode 100644
index 0000000..b3767fe
--- /dev/null
+++ b/drivers/firmware/iscsi_ibft.c
@@ -0,0 +1,201 @@
+/*
+ * drivers/firmware/iscsi_ibft.c
+ * Copyright 2007 Red Hat, Inc.
+ * by Peter Jones <pjones@xxxxxxxxxx>
+ * Copyright 2007 IBM
+ * by Konrad Rzeszutek <konradr@xxxxxxxxxx>
+ *
+ * This code exposes the 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 <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+
+#include <linux/iscsi_ibft.h>
+
+#define ISCSI_IBFT_VERSION "0.2"
+#define ISCSI_IBFT_DATE "2007-Aug-29"
+
+MODULE_AUTHOR
+("Peter Jones <pjones@xxxxxxxxxx> and Konrad Rzeszutek <konradr@xxxxxxxxxx>");
+MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ISCSI_IBFT_VERSION);
+
+
+static void ibft_release(struct kobject *kobj)
+{
+ struct ibft_device *ibft = container_of(kobj, struct ibft_device, kobj);
+ kfree(ibft->hdr);
+ kfree(ibft);
+}
+
+static ssize_t
+ibft_read_binary(struct kobject *kobj, struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+
+ struct ibft_device *ibft = container_of(kobj, struct ibft_device, kobj);
+ ssize_t len = ibft->hdr->length;
+
+ if (off > len)
+ return 0;
+
+ if (off + count > len)
+ count = len - off;
+
+ memcpy(buf, ibft->hdr + off, count);
+
+ return count;
+}
+static int
+ibft_mmap_binary(struct kobject *kobj, struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ struct ibft_device *ibft = container_of(kobj, struct ibft_device, kobj);
+ ssize_t len = ibft->hdr->length;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long pos;
+ unsigned long pfn;
+ int i;
+
+ pos = (unsigned long)ibft->hdr;
+
+ if (off > len)
+ return -EINVAL;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EPERM;
+
+ for (i = 0; i < len; i += PAGE_SIZE) {
+ pfn = virt_to_phys((void *)(pos + off)) >> PAGE_SHIFT;
+ if (remap_pfn_range
+ (vma, start, pfn, PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ if (size <= PAGE_SIZE)
+ break;
+ size -= PAGE_SIZE;
+ }
+ return 0;
+}
+static struct bin_attribute ibft_attribute_binary = {
+ .attr = {
+ .name = "binary",
+ .mode = S_IRUSR,
+ .owner = THIS_MODULE,
+ },
+ .read = ibft_read_binary,
+ .write = NULL,
+ .mmap = ibft_mmap_binary
+};
+static struct kobj_type ktype_ibft = {
+ .release = ibft_release,
+};
+
+static decl_subsys(ibft, &ktype_ibft, NULL);
+
+static int ibft_device_register(struct ibft_device *idev)
+{
+ int error = 0;
+ int len = 0;
+ struct ibft_header *hdr;
+
+ if (!idev)
+ return 1;
+
+ /* Copy over the data */
+ hdr = (struct ibft_header *)phys_to_virt((unsigned long)ibft_phys);
+ len = hdr->length;
+
+ /* Need PAGE_ALING for mmap functionality. */
+ idev->hdr = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
+ if (!idev->hdr)
+ return -ENOMEM;
+
+ memcpy(idev->hdr, hdr, len);
+
+ /* This is firmware/ibft */
+ kobject_set_name(&idev->kobj, "table");
+ kobj_set_kset_s(idev, ibft_subsys);
+ error = kobject_register(&idev->kobj);
+
+ if (!error) {
+ ibft_attribute_binary.size = idev->hdr->length;
+ error =
+ sysfs_create_bin_file(&idev->kobj, &ibft_attribute_binary);
+ }
+
+ /* The de-allocation part is done by module_exit() */
+ return error;
+}
+
+static struct ibft_device *ibft_idev;
+/*
+ * ibft_init() - creates sysfs tree entry for ibft data
+ */
+static int __init ibft_init(void)
+{
+ int rc = 0;
+
+ printk(KERN_INFO "BIOS iBFT facility v%s %s\n", ISCSI_IBFT_VERSION,
+ ISCSI_IBFT_DATE);
+
+ if (!ibft_phys)
+ find_ibft();
+
+ /* What if the ibft_subsys is underneath another struct? */
+ 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);
+ ibft_idev = kzalloc(sizeof(*ibft_idev), GFP_KERNEL);
+ if (!ibft_idev)
+ return -ENOMEM;
+
+ rc = ibft_device_register(ibft_idev);
+ if (rc) {
+ kfree(ibft_idev);
+ return rc;
+ }
+ } else {
+ printk(KERN_INFO "No iBFT detected.\n");
+ }
+ return rc;
+}
+
+static void __exit ibft_exit(void)
+{
+ if (ibft_idev)
+ kobject_unregister(&ibft_idev->kobj);
+
+ firmware_unregister(&ibft_subsys);
+ printk(KERN_INFO "BIOS iBFT unloaded.\n");
+}
+
+module_init(ibft_init);
+module_exit(ibft_exit);
diff --git a/include/linux/iscsi_ibft.h b/include/linux/iscsi_ibft.h
new file mode 100644
index 0000000..5e7b267
--- /dev/null
+++ b/include/linux/iscsi_ibft.h
@@ -0,0 +1,58 @@
+#ifndef ISCSI_IBFT_H
+#define ISCSI_IBFT_H
+
+extern void *ibft_phys;
+
+struct ibft_header {
+ char signature[4];
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ char reserved[24];
+};
+
+struct ibft_device {
+ struct ibft_header *hdr;
+ struct kobject kobj;
+};
+
+#if defined(CONFIG_ISCSI_IBFT) || defined(CONFIG_ISCSI_IBFT_MODULES)
+
+#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 /* 132kB */
+static inline 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-PAGE_SIZE)
+ pos += VGA_SIZE+PAGE_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;
+}
+
+#else
+
+static inline ssize_t find_ibft(void) { return 0; };
+#endif
+#endif /* ISCSI_IBFT_H */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/