2008-04-10 06:50:41 +04:00
/*
2010-02-25 23:37:17 +03:00
* Copyright 2007 - 2010 Red Hat , Inc .
2008-04-10 06:50:41 +04:00
* by Peter Jones < pjones @ redhat . com >
* Copyright 2008 IBM , Inc .
* by Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Copyright 2008
* by Konrad Rzeszutek < ketuzsezr @ darnok . org >
*
* 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 .
*
* Changelog :
*
2010-02-25 23:37:17 +03:00
* 06 Jan 2010 - Peter Jones < pjones @ redhat . com >
* New changelog entries are in the git log from now on . Not here .
*
2008-04-10 06:50:41 +04:00
* 14 Mar 2008 - Konrad Rzeszutek < ketuzsezr @ darnok . org >
* Updated comments and copyrights . ( v0 .4 .9 )
*
* 11 Feb 2008 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Converted to using ibft_addr . ( v0 .4 .8 )
*
* 8 Feb 2008 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Combined two functions in one : reserve_ibft_region . ( v0 .4 .7 )
*
* 30 Jan 2008 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added logic to handle IPv6 addresses . ( v0 .4 .6 )
*
* 25 Jan 2008 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added logic to handle badly not - to - spec iBFT . ( v0 .4 .5 )
*
* 4 Jan 2008 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added __init to function declarations . ( v0 .4 .4 )
*
* 21 Dec 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Updated kobject registration , combined unregister functions in one
* and code and style cleanup . ( v0 .4 .3 )
*
* 5 Dec 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added end - markers to enums and re - organized kobject registration . ( v0 .4 .2 )
*
* 4 Dec 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Created ' device ' sysfs link to the NIC and style cleanup . ( v0 .4 .1 )
*
* 28 Nov 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added sysfs - ibft documentation , moved ' find_ibft ' function to
* in its own file and added text attributes for every struct field . ( v0 .4 )
*
* 21 Nov 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added text attributes emulating OpenFirmware / proc / device - tree naming .
* Removed binary / sysfs interface ( v0 .3 )
*
* 29 Aug 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* Added functionality in setup . c to reserve iBFT region . ( v0 .2 )
*
* 27 Aug 2007 - Konrad Rzeszutek < konradr @ linux . vnet . ibm . com >
* First version exposing iBFT data via a binary / sysfs . ( v0 .1 )
*
*/
# include <linux/blkdev.h>
# include <linux/capability.h>
# include <linux/ctype.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/iscsi_ibft.h>
# include <linux/limits.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/slab.h>
# include <linux/stat.h>
# include <linux/string.h>
# include <linux/types.h>
2010-02-25 23:37:17 +03:00
# include <linux/acpi.h>
2010-04-12 22:06:18 +04:00
# include <linux/iscsi_boot_sysfs.h>
2008-04-10 06:50:41 +04:00
2010-02-25 23:37:17 +03:00
# define IBFT_ISCSI_VERSION "0.5.0"
# define IBFT_ISCSI_DATE "2010-Feb-25"
2008-04-10 06:50:41 +04:00
2011-04-24 07:38:19 +04:00
MODULE_AUTHOR ( " Peter Jones <pjones@redhat.com> and "
" Konrad Rzeszutek <ketuzsezr@darnok.org> " ) ;
2008-04-10 06:50:41 +04:00
MODULE_DESCRIPTION ( " sysfs interface to BIOS iBFT information " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( IBFT_ISCSI_VERSION ) ;
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 different types and its names .
*
*/
enum ibft_id {
id_reserved = 0 , /* We don't support. */
id_control = 1 , /* Should show up only once and is not exported. */
id_initiator = 2 ,
id_nic = 3 ,
id_target = 4 ,
id_extensions = 5 , /* We don't support. */
id_end_marker ,
} ;
/*
* The kobject and attribute structures .
*/
struct ibft_kobject {
2010-02-25 23:37:17 +03:00
struct acpi_table_ibft * header ;
2008-04-10 06:50:41 +04:00
union {
struct ibft_initiator * initiator ;
struct ibft_nic * nic ;
struct ibft_tgt * tgt ;
struct ibft_hdr * hdr ;
} ;
} ;
2010-04-12 22:06:18 +04:00
static struct iscsi_boot_kset * boot_kset ;
2008-04-10 06:50:41 +04:00
2015-05-28 22:51:51 +03:00
/* fully null address */
2008-04-10 06:50:41 +04:00
static const char nulls [ 16 ] ;
2015-05-28 22:51:51 +03:00
/* IPv4-mapped IPv6 ::ffff:0.0.0.0 */
static const char mapped_nulls [ 16 ] = { 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0xff , 0xff ,
0x00 , 0x00 , 0x00 , 0x00 } ;
static int address_not_null ( u8 * ip )
{
return ( memcmp ( ip , nulls , 16 ) & & memcmp ( ip , mapped_nulls , 16 ) ) ;
}
2008-04-10 06:50:41 +04:00
/*
* Helper functions to parse data properly .
*/
static ssize_t sprintf_ipaddr ( char * buf , u8 * ip )
{
char * str = buf ;
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
*/
2008-10-31 10:56:00 +03:00
str + = sprintf ( buf , " %pI4 " , ip + 12 ) ;
2008-04-10 06:50:41 +04:00
} else {
/*
* IPv6
*/
2008-10-29 22:52:50 +03:00
str + = sprintf ( str , " %pI6 " , ip ) ;
2008-04-10 06:50:41 +04:00
}
str + = sprintf ( str , " \n " ) ;
return str - buf ;
}
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 ( char * t , struct ibft_hdr * hdr , int id , int length )
{
if ( hdr - > id ! = id ) {
2010-04-12 22:06:18 +04:00
printk ( KERN_ERR " iBFT error: We expected the %s " \
2008-04-10 06:50:41 +04:00
" field header.id to have %d but " \
2010-04-12 22:06:18 +04:00
" found %d instead! \n " , t , id , hdr - > id ) ;
2008-04-10 06:50:41 +04:00
return - ENODEV ;
}
if ( hdr - > length ! = length ) {
2010-04-12 22:06:18 +04:00
printk ( KERN_ERR " iBFT error: We expected the %s " \
2008-04-10 06:50:41 +04:00
" field header.length to have %d but " \
2010-04-12 22:06:18 +04:00
" found %d instead! \n " , t , length , hdr - > length ) ;
2008-04-10 06:50:41 +04:00
return - ENODEV ;
}
return 0 ;
}
/*
* Routines for parsing the iBFT data to be human readable .
*/
2010-04-12 22:06:18 +04:00
static ssize_t ibft_attr_show_initiator ( void * data , int type , char * buf )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
struct ibft_kobject * entry = data ;
2008-04-10 06:50:41 +04:00
struct ibft_initiator * initiator = entry - > initiator ;
void * ibft_loc = entry - > header ;
char * str = buf ;
if ( ! initiator )
return 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_INI_INDEX :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , initiator - > hdr . index ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_FLAGS :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , initiator - > hdr . flags ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_ISNS_SERVER :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , initiator - > isns_server ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_SLP_SERVER :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , initiator - > slp_server ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_PRI_RADIUS_SERVER :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , initiator - > pri_radius_server ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_SEC_RADIUS_SERVER :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , initiator - > sec_radius_server ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_INI_INITIATOR_NAME :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , initiator - > initiator_name_len ,
( char * ) ibft_loc +
initiator - > initiator_name_off ) ;
break ;
default :
break ;
}
return str - buf ;
}
2010-04-12 22:06:18 +04:00
static ssize_t ibft_attr_show_nic ( void * data , int type , char * buf )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
struct ibft_kobject * entry = data ;
2008-04-10 06:50:41 +04:00
struct ibft_nic * nic = entry - > nic ;
void * ibft_loc = entry - > header ;
char * str = buf ;
2010-01-14 19:34:19 +03:00
__be32 val ;
2008-04-10 06:50:41 +04:00
if ( ! nic )
return 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_ETH_INDEX :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , nic - > hdr . index ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_FLAGS :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , nic - > hdr . flags ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_IP_ADDR :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , nic - > ip_addr ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_SUBNET_MASK :
2010-01-14 19:34:19 +03:00
val = cpu_to_be32 ( ~ ( ( 1 < < ( 32 - nic - > subnet_mask_prefix ) ) - 1 ) ) ;
str + = sprintf ( str , " %pI4 " , & val ) ;
2008-04-10 06:50:41 +04:00
break ;
2016-02-25 20:56:04 +03:00
case ISCSI_BOOT_ETH_PREFIX_LEN :
str + = sprintf ( str , " %d \n " , nic - > subnet_mask_prefix ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_ORIGIN :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , nic - > origin ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_GATEWAY :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , nic - > gateway ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_PRIMARY_DNS :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , nic - > primary_dns ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_SECONDARY_DNS :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , nic - > secondary_dns ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_DHCP :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , nic - > dhcp ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_VLAN :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , nic - > vlan ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_MAC :
2010-01-05 09:37:43 +03:00
str + = sprintf ( str , " %pM \n " , nic - > mac ) ;
2008-04-10 06:50:41 +04:00
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_HOSTNAME :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , nic - > hostname_len ,
( char * ) ibft_loc + nic - > hostname_off ) ;
break ;
default :
break ;
}
return str - buf ;
} ;
2010-04-12 22:06:18 +04:00
static ssize_t ibft_attr_show_target ( void * data , int type , char * buf )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
struct ibft_kobject * entry = data ;
2008-04-10 06:50:41 +04:00
struct ibft_tgt * tgt = entry - > tgt ;
void * ibft_loc = entry - > header ;
char * str = buf ;
int i ;
if ( ! tgt )
return 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_TGT_INDEX :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , tgt - > hdr . index ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_FLAGS :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , tgt - > hdr . flags ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_IP_ADDR :
2008-04-10 06:50:41 +04:00
str + = sprintf_ipaddr ( str , tgt - > ip_addr ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_PORT :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , tgt - > port ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_LUN :
2008-04-10 06:50:41 +04:00
for ( i = 0 ; i < 8 ; i + + )
str + = sprintf ( str , " %x " , ( u8 ) tgt - > lun [ i ] ) ;
str + = sprintf ( str , " \n " ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_NIC_ASSOC :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , tgt - > nic_assoc ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_CHAP_TYPE :
2008-04-10 06:50:41 +04:00
str + = sprintf ( str , " %d \n " , tgt - > chap_type ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_NAME :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , tgt - > tgt_name_len ,
( char * ) ibft_loc + tgt - > tgt_name_off ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_CHAP_NAME :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , tgt - > chap_name_len ,
( char * ) ibft_loc + tgt - > chap_name_off ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_CHAP_SECRET :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , tgt - > chap_secret_len ,
( char * ) ibft_loc + tgt - > chap_secret_off ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_REV_CHAP_NAME :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , tgt - > rev_chap_name_len ,
( char * ) ibft_loc +
tgt - > rev_chap_name_off ) ;
break ;
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_TGT_REV_CHAP_SECRET :
2008-04-10 06:50:41 +04:00
str + = sprintf_string ( str , tgt - > rev_chap_secret_len ,
( char * ) ibft_loc +
tgt - > rev_chap_secret_off ) ;
break ;
default :
break ;
}
return str - buf ;
}
2016-03-24 04:49:26 +03:00
static ssize_t ibft_attr_show_acpitbl ( void * data , int type , char * buf )
{
struct ibft_kobject * entry = data ;
char * str = buf ;
switch ( type ) {
case ISCSI_BOOT_ACPITBL_SIGNATURE :
str + = sprintf_string ( str , ACPI_NAME_SIZE ,
entry - > header - > header . signature ) ;
break ;
case ISCSI_BOOT_ACPITBL_OEM_ID :
str + = sprintf_string ( str , ACPI_OEM_ID_SIZE ,
entry - > header - > header . oem_id ) ;
break ;
case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID :
str + = sprintf_string ( str , ACPI_OEM_TABLE_ID_SIZE ,
entry - > header - > header . oem_table_id ) ;
break ;
default :
break ;
}
return str - buf ;
}
2008-04-10 06:50:41 +04:00
static int __init ibft_check_device ( void )
{
int len ;
u8 * pos ;
u8 csum = 0 ;
2010-02-25 23:37:17 +03:00
len = ibft_addr - > header . length ;
2008-04-10 06:50:41 +04:00
/* Sanity checking of iBFT. */
2010-02-25 23:37:17 +03:00
if ( ibft_addr - > header . revision ! = 1 ) {
2008-04-10 06:50:41 +04:00
printk ( KERN_ERR " iBFT module supports only revision 1, " \
2010-02-25 23:37:17 +03:00
" while this is %d. \n " ,
ibft_addr - > header . revision ) ;
2008-04-10 06:50:41 +04:00
return - ENOENT ;
}
for ( pos = ( u8 * ) ibft_addr ; pos < ( u8 * ) ibft_addr + len ; pos + + )
csum + = * pos ;
if ( csum ) {
printk ( KERN_ERR " iBFT has incorrect checksum (0x%x)! \n " , csum ) ;
return - ENOENT ;
}
return 0 ;
}
2010-04-12 22:06:18 +04:00
/*
* Helper routiners to check to determine if the entry is valid
* in the proper iBFT structure .
*/
2011-07-24 07:11:19 +04:00
static umode_t ibft_check_nic_for ( void * data , int type )
2010-04-12 22:06:18 +04:00
{
struct ibft_kobject * entry = data ;
struct ibft_nic * nic = entry - > nic ;
2011-07-24 07:11:19 +04:00
umode_t rc = 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_ETH_INDEX :
case ISCSI_BOOT_ETH_FLAGS :
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_IP_ADDR :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( nic - > ip_addr ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
2016-02-25 20:56:04 +03:00
case ISCSI_BOOT_ETH_PREFIX_LEN :
2010-04-12 22:06:18 +04:00
case ISCSI_BOOT_ETH_SUBNET_MASK :
if ( nic - > subnet_mask_prefix )
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_ORIGIN :
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_GATEWAY :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( nic - > gateway ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_PRIMARY_DNS :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( nic - > primary_dns ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_SECONDARY_DNS :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( nic - > secondary_dns ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_DHCP :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( nic - > dhcp ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_VLAN :
case ISCSI_BOOT_ETH_MAC :
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_ETH_HOSTNAME :
if ( nic - > hostname_off )
rc = S_IRUGO ;
break ;
default :
break ;
}
return rc ;
}
2011-07-24 07:11:19 +04:00
static umode_t __init ibft_check_tgt_for ( void * data , int type )
2010-04-12 22:06:18 +04:00
{
struct ibft_kobject * entry = data ;
struct ibft_tgt * tgt = entry - > tgt ;
2011-07-24 07:11:19 +04:00
umode_t rc = 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_TGT_INDEX :
case ISCSI_BOOT_TGT_FLAGS :
case ISCSI_BOOT_TGT_IP_ADDR :
case ISCSI_BOOT_TGT_PORT :
case ISCSI_BOOT_TGT_LUN :
case ISCSI_BOOT_TGT_NIC_ASSOC :
case ISCSI_BOOT_TGT_CHAP_TYPE :
rc = S_IRUGO ;
case ISCSI_BOOT_TGT_NAME :
if ( tgt - > tgt_name_len )
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_TGT_CHAP_NAME :
case ISCSI_BOOT_TGT_CHAP_SECRET :
if ( tgt - > chap_name_len )
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_TGT_REV_CHAP_NAME :
case ISCSI_BOOT_TGT_REV_CHAP_SECRET :
if ( tgt - > rev_chap_name_len )
rc = S_IRUGO ;
break ;
default :
break ;
}
return rc ;
}
2011-07-24 07:11:19 +04:00
static umode_t __init ibft_check_initiator_for ( void * data , int type )
2010-04-12 22:06:18 +04:00
{
struct ibft_kobject * entry = data ;
struct ibft_initiator * init = entry - > initiator ;
2011-07-24 07:11:19 +04:00
umode_t rc = 0 ;
2010-04-12 22:06:18 +04:00
switch ( type ) {
case ISCSI_BOOT_INI_INDEX :
case ISCSI_BOOT_INI_FLAGS :
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_INI_ISNS_SERVER :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( init - > isns_server ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_INI_SLP_SERVER :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( init - > slp_server ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_INI_PRI_RADIUS_SERVER :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( init - > pri_radius_server ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_INI_SEC_RADIUS_SERVER :
2015-05-28 22:51:51 +03:00
if ( address_not_null ( init - > sec_radius_server ) )
2010-04-12 22:06:18 +04:00
rc = S_IRUGO ;
break ;
case ISCSI_BOOT_INI_INITIATOR_NAME :
if ( init - > initiator_name_len )
rc = S_IRUGO ;
break ;
default :
break ;
}
return rc ;
}
2016-03-24 04:49:26 +03:00
static umode_t __init ibft_check_acpitbl_for ( void * data , int type )
{
umode_t rc = 0 ;
switch ( type ) {
case ISCSI_BOOT_ACPITBL_SIGNATURE :
case ISCSI_BOOT_ACPITBL_OEM_ID :
case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID :
rc = S_IRUGO ;
break ;
default :
break ;
}
return rc ;
}
2011-06-25 00:11:53 +04:00
static void ibft_kobj_release ( void * data )
{
kfree ( data ) ;
}
2008-04-10 06:50:41 +04:00
/*
* Helper function for ibft_register_kobjects .
*/
2010-02-25 23:37:17 +03:00
static int __init ibft_create_kobject ( struct acpi_table_ibft * header ,
2010-04-12 22:06:18 +04:00
struct ibft_hdr * hdr )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
struct iscsi_boot_kobj * boot_kobj = NULL ;
2008-04-10 06:50:41 +04:00
struct ibft_kobject * ibft_kobj = NULL ;
struct ibft_nic * nic = ( struct ibft_nic * ) hdr ;
struct pci_dev * pci_dev ;
int rc = 0 ;
ibft_kobj = kzalloc ( sizeof ( * ibft_kobj ) , GFP_KERNEL ) ;
if ( ! ibft_kobj )
return - ENOMEM ;
ibft_kobj - > header = header ;
ibft_kobj - > hdr = hdr ;
switch ( hdr - > id ) {
case id_initiator :
rc = ibft_verify_hdr ( " initiator " , hdr , id_initiator ,
sizeof ( * ibft_kobj - > initiator ) ) ;
2010-04-12 22:06:18 +04:00
if ( rc )
break ;
boot_kobj = iscsi_boot_create_initiator ( boot_kset , hdr - > index ,
ibft_kobj ,
ibft_attr_show_initiator ,
2011-06-25 00:11:53 +04:00
ibft_check_initiator_for ,
ibft_kobj_release ) ;
2010-04-12 22:06:18 +04:00
if ( ! boot_kobj ) {
rc = - ENOMEM ;
goto free_ibft_obj ;
}
2008-04-10 06:50:41 +04:00
break ;
case id_nic :
rc = ibft_verify_hdr ( " ethernet " , hdr , id_nic ,
sizeof ( * ibft_kobj - > nic ) ) ;
2010-04-12 22:06:18 +04:00
if ( rc )
break ;
boot_kobj = iscsi_boot_create_ethernet ( boot_kset , hdr - > index ,
ibft_kobj ,
ibft_attr_show_nic ,
2011-06-25 00:11:53 +04:00
ibft_check_nic_for ,
ibft_kobj_release ) ;
2010-04-12 22:06:18 +04:00
if ( ! boot_kobj ) {
rc = - ENOMEM ;
goto free_ibft_obj ;
}
2008-04-10 06:50:41 +04:00
break ;
case id_target :
rc = ibft_verify_hdr ( " target " , hdr , id_target ,
sizeof ( * ibft_kobj - > tgt ) ) ;
2010-04-12 22:06:18 +04:00
if ( rc )
break ;
boot_kobj = iscsi_boot_create_target ( boot_kset , hdr - > index ,
ibft_kobj ,
ibft_attr_show_target ,
2011-06-25 00:11:53 +04:00
ibft_check_tgt_for ,
ibft_kobj_release ) ;
2010-04-12 22:06:18 +04:00
if ( ! boot_kobj ) {
rc = - ENOMEM ;
goto free_ibft_obj ;
}
2008-04-10 06:50:41 +04:00
break ;
case id_reserved :
case id_control :
case id_extensions :
/* Fields which we don't support. Ignore them */
rc = 1 ;
break ;
default :
printk ( KERN_ERR " iBFT has unknown structure type (%d). " \
" Report this bug to %.6s! \n " , hdr - > id ,
2010-02-25 23:37:17 +03:00
header - > header . oem_id ) ;
2008-04-10 06:50:41 +04:00
rc = 1 ;
break ;
}
if ( rc ) {
/* Skip adding this kobject, but exit with non-fatal error. */
2010-04-12 22:06:18 +04:00
rc = 0 ;
goto free_ibft_obj ;
2008-04-10 06:50:41 +04:00
}
if ( hdr - > id = = id_nic ) {
/*
* We don ' t search for the device in other domains than
* zero . This is because on x86 platforms the BIOS
* executes only devices which are in domain 0. Furthermore , the
* iBFT spec doesn ' t have a domain id field : - (
*/
2017-12-19 08:37:43 +03:00
pci_dev = pci_get_domain_bus_and_slot ( 0 ,
( nic - > pci_bdf & 0xff00 ) > > 8 ,
( nic - > pci_bdf & 0xff ) ) ;
2008-04-10 06:50:41 +04:00
if ( pci_dev ) {
2010-04-12 22:06:18 +04:00
rc = sysfs_create_link ( & boot_kobj - > kobj ,
2008-04-10 06:50:41 +04:00
& pci_dev - > dev . kobj , " device " ) ;
pci_dev_put ( pci_dev ) ;
}
}
2010-04-12 22:06:18 +04:00
return 0 ;
2008-04-10 06:50:41 +04:00
2010-04-12 22:06:18 +04:00
free_ibft_obj :
kfree ( ibft_kobj ) ;
2008-04-10 06:50:41 +04:00
return rc ;
}
/*
* Scan the IBFT table structure for the NIC and Target fields . When
* found add them on the passed - in list . We do not support the other
* fields at this point , so they are skipped .
*/
2010-04-12 22:06:18 +04:00
static int __init ibft_register_kobjects ( struct acpi_table_ibft * header )
2008-04-10 06:50:41 +04:00
{
struct ibft_control * control = NULL ;
2016-03-24 04:49:26 +03:00
struct iscsi_boot_kobj * boot_kobj ;
struct ibft_kobject * ibft_kobj ;
2008-04-10 06:50:41 +04:00
void * ptr , * end ;
int rc = 0 ;
u16 offset ;
u16 eot_offset ;
control = ( void * ) header + sizeof ( * header ) ;
end = ( void * ) control + control - > hdr . length ;
2010-02-25 23:37:17 +03:00
eot_offset = ( void * ) header + header - > header . length - ( void * ) control ;
2008-04-10 06:50:41 +04:00
rc = ibft_verify_hdr ( " control " , ( struct ibft_hdr * ) control , id_control ,
sizeof ( * control ) ) ;
/* iBFT table safety checking */
rc | = ( ( control - > hdr . index ) ? - ENODEV : 0 ) ;
if ( rc ) {
printk ( KERN_ERR " iBFT error: Control header is invalid! \n " ) ;
return rc ;
}
for ( ptr = & control - > initiator_off ; ptr < end ; ptr + = sizeof ( u16 ) ) {
offset = * ( u16 * ) ptr ;
2010-02-25 23:37:17 +03:00
if ( offset & & offset < header - > header . length & &
offset < eot_offset ) {
2008-04-10 06:50:41 +04:00
rc = ibft_create_kobject ( header ,
2010-04-12 22:06:18 +04:00
( void * ) header + offset ) ;
2008-04-10 06:50:41 +04:00
if ( rc )
break ;
}
}
2016-03-24 04:49:26 +03:00
if ( rc )
return rc ;
ibft_kobj = kzalloc ( sizeof ( * ibft_kobj ) , GFP_KERNEL ) ;
if ( ! ibft_kobj )
return - ENOMEM ;
ibft_kobj - > header = header ;
ibft_kobj - > hdr = NULL ; /*for ibft_unregister*/
boot_kobj = iscsi_boot_create_acpitbl ( boot_kset , 0 ,
ibft_kobj ,
ibft_attr_show_acpitbl ,
ibft_check_acpitbl_for ,
ibft_kobj_release ) ;
if ( ! boot_kobj ) {
kfree ( ibft_kobj ) ;
rc = - ENOMEM ;
}
2008-04-10 06:50:41 +04:00
return rc ;
}
2010-04-12 22:06:18 +04:00
static void ibft_unregister ( void )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
struct iscsi_boot_kobj * boot_kobj , * tmp_kobj ;
struct ibft_kobject * ibft_kobj ;
list_for_each_entry_safe ( boot_kobj , tmp_kobj ,
& boot_kset - > kobj_list , list ) {
ibft_kobj = boot_kobj - > data ;
2016-03-24 04:49:26 +03:00
if ( ibft_kobj - > hdr & & ibft_kobj - > hdr - > id = = id_nic )
2010-04-12 22:06:18 +04:00
sysfs_remove_link ( & boot_kobj - > kobj , " device " ) ;
2008-04-10 06:50:41 +04:00
} ;
}
2010-04-12 22:06:18 +04:00
static void ibft_cleanup ( void )
2008-04-10 06:50:41 +04:00
{
2010-08-12 00:35:40 +04:00
if ( boot_kset ) {
ibft_unregister ( ) ;
iscsi_boot_destroy_kset ( boot_kset ) ;
}
2008-04-10 06:50:41 +04:00
}
2010-04-12 22:06:18 +04:00
static void __exit ibft_exit ( void )
2008-04-10 06:50:41 +04:00
{
2010-04-12 22:06:18 +04:00
ibft_cleanup ( ) ;
2008-04-10 06:50:41 +04:00
}
2011-12-13 00:39:14 +04:00
# ifdef CONFIG_ACPI
static const struct {
char * sign ;
} ibft_signs [ ] = {
/*
* One spec says " IBFT " , the other says " iBFT " . We have to check
* for both .
*/
{ ACPI_SIG_IBFT } ,
{ " iBFT " } ,
2014-05-13 15:41:32 +04:00
{ " BIFT " } , /* Broadcom iSCSI Offload */
2011-12-13 00:39:14 +04:00
} ;
static void __init acpi_find_ibft_region ( void )
{
int i ;
struct acpi_table_header * table = NULL ;
if ( acpi_disabled )
return ;
for ( i = 0 ; i < ARRAY_SIZE ( ibft_signs ) & & ! ibft_addr ; i + + ) {
acpi_get_table ( ibft_signs [ i ] . sign , 0 , & table ) ;
ibft_addr = ( struct acpi_table_ibft * ) table ;
}
}
# else
static void __init acpi_find_ibft_region ( void )
{
}
# endif
2008-04-10 06:50:41 +04:00
/*
* ibft_init ( ) - creates sysfs tree entries for the iBFT data .
*/
static int __init ibft_init ( void )
{
int rc = 0 ;
2011-12-13 00:39:14 +04:00
/*
As on UEFI systems the setup_arch ( ) / find_ibft_region ( )
is called before ACPI tables are parsed and it only does
legacy finding .
*/
if ( ! ibft_addr )
acpi_find_ibft_region ( ) ;
2008-04-10 06:50:41 +04:00
if ( ibft_addr ) {
2011-12-13 00:39:14 +04:00
pr_info ( " iBFT detected. \n " ) ;
2008-04-10 06:50:41 +04:00
rc = ibft_check_device ( ) ;
if ( rc )
2010-04-12 22:06:18 +04:00
return rc ;
2008-04-10 06:50:41 +04:00
2010-04-12 22:06:18 +04:00
boot_kset = iscsi_boot_create_kset ( " ibft " ) ;
if ( ! boot_kset )
return - ENOMEM ;
2008-04-10 06:50:41 +04:00
2010-04-12 22:06:18 +04:00
/* Scan the IBFT for data and register the kobjects. */
rc = ibft_register_kobjects ( ibft_addr ) ;
2008-04-10 06:50:41 +04:00
if ( rc )
goto out_free ;
} else
printk ( KERN_INFO " No iBFT detected. \n " ) ;
return 0 ;
out_free :
2010-04-12 22:06:18 +04:00
ibft_cleanup ( ) ;
2008-04-10 06:50:41 +04:00
return rc ;
}
module_init ( ibft_init ) ;
module_exit ( ibft_exit ) ;