The patch below has been tested on a bunch of HP, Dell, NEC, and IBM machines by me and works without trouble. I was humbly wondering if Fedora kernel community would be OK picking this patch it up so that it can be tested on some other platforms before I go forth on LKML with it? And obviously, any comments on the patch are much appreciated. The testing is to make sure that this patch does not panic the box when it scans the memory (find_ibft function). For those that don't know what iSCSI is, here is a nice description: http://en.wikipedia.org/wiki/ISCSI and here http://linux-iscsi.sourceforge.net/ The issue at hand is to support iSCSI boot automatically. Right now when Fedora Core is installed on a iSCSI target and booted up from a NIC that supports software iSCSI initiator (the NIC firmware "exports" a disk) all the connection information (IQN, IP, CHAP password, etc) are all hard-coded in the initrd. This is OK, except when the password changes (and programmed in the NIC) the initrd can't boot anymore (unless the system admin also logged in the machine and changed the settings and re-created the initrd before rebooting the box). The goal is to have the initrd able to extract the iSCSI information from the NIC and use them. The patch below is one step in making this goal. The other necessary parts for this are to have iscsi-initirator-utils support this (upstream has the patches for this), and anaconda (not yet there). Thank you for your time. ----- PATCH: ----- 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. To enable this please set CONFIG_ISCSI_IBFT to m. 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 */ -- Konrad Rzeszutek 1-(978)-392-3903 or 1-(617)-693-1718 IBM on-site partner. _______________________________________________ Fedora-kernel-list mailing list Fedora-kernel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/fedora-kernel-list