OpenSolaris_b135/uts/i86pc/os/ibft.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This is the place to implement ld_ib_props()
 * For x86 it is to load iBFT and costruct the global ib props
 */

#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/mman.h>
#include <sys/bootprops.h>
#include <sys/kmem.h>
#include <sys/psm.h>
#include <sys/bootconf.h>

#ifndef	NULL
#define	NULL	0
#endif

typedef enum ibft_structure_type {
	Reserved	=	0,
	Control		=	1,
	Initiator	=	2,
	Nic		=	3,
	Target		=	4,
	Extensions	=	5,
	Type_End
}ibft_struct_type;

typedef enum _chap_type {
	NO_CHAP		=	0,
	CHAP		=	1,
	Mutual_CHAP	=	2,
	TYPE_UNKNOWN
}chap_type;

typedef struct ibft_entry {
	int	af;
	int	e_port;
	char	target_name[224];
	char	target_addr[INET6_ADDRSTRLEN];
}ibft_entry_t;

typedef struct iSCSI_ibft_tbl_hdr {
	char	    Signature[4];
	int	    Length;
	char	    Revision;
	char	    Checksum;
	char	    oem_id[6];
	char	    oem_table_id[8];
	char	    Reserved[24];
}iscsi_ibft_tbl_hdr_t;

typedef struct iSCSI_ibft_hdr {
	char	    Structure_id;
	char	    Version;
	ushort_t    Length;
	char	    Index;
	char	    Flags;
}iscsi_ibft_hdr_t;

typedef struct iSCSI_ibft_control {
	iscsi_ibft_hdr_t    header;
	ushort_t	    Extensions;
	ushort_t	    Initiator_offset;
	ushort_t	    Nic0_offset;
	ushort_t	    Target0_offset;
	ushort_t	    Nic1_offset;
	ushort_t	    Target1_offset;
}iscsi_ibft_ctl_t;

typedef struct iSCSI_ibft_initiator {
	iscsi_ibft_hdr_t    header;
	uchar_t		    iSNS_Server[16];
	uchar_t		    SLP_Server[16];
	uchar_t		    Pri_Radius_Server[16];
	uchar_t		    Sec_Radius_Server[16];
	ushort_t	    ini_name_len;
	ushort_t	    ini_name_offset;
}iscsi_ibft_initiator_t;

typedef struct iSCSI_ibft_nic {
	iscsi_ibft_hdr_t    header;
	uchar_t		    ip_addr[16];
	char		    Subnet_Mask_Prefix;
	char		    Origin;
	uchar_t		    Gateway[16];
	uchar_t		    Primary_dns[16];
	uchar_t		    Secondary_dns[16];
	uchar_t		    dhcp[16];
	ushort_t	    vlan;
	char		    mac[6];
	ushort_t	    pci_BDF;
	ushort_t	    Hostname_len;
	ushort_t	    Hostname_offset;
}iscsi_ibft_nic_t;

typedef struct iSCSI_ibft_target {
	iscsi_ibft_hdr_t    header;
	uchar_t		    ip_addr[16];
	ushort_t	    port;
	uchar_t		    boot_lun[8];
	uchar_t		    chap_type;
	uchar_t		    nic_association;
	ushort_t	    target_name_len;
	ushort_t	    target_name_offset;
	ushort_t	    chap_name_len;
	ushort_t	    chap_name_offset;
	ushort_t	    chap_secret_len;
	ushort_t	    chap_secret_offset;
	ushort_t	    rev_chap_name_len;
	ushort_t	    rev_chap_name_offset;
	ushort_t	    rev_chap_secret_len;
	ushort_t	    rev_chap_secret_offset;
}iscsi_ibft_tgt_t;

#define	ISCSI_IBFT_LOWER_ADDR		0x80000	    /* 512K */
#define	ISCSI_IBFT_HIGHER_ADDR		0x100000    /* 1024K */
#define	ISCSI_IBFT_SIGNATRUE		"iBFT"
#define	ISCSI_IBFT_SIGNATURE_LEN	4
#define	ISCSI_IBFT_TBL_BUF_LEN		1024
#define	ISCSI_IBFT_ALIGNED		16
#define	ISCSI_IBFT_CTL_OFFSET		48

#define	IBFT_BLOCK_VALID_YES		0x01	/* bit 0 */
#define	IBFT_FIRMWARE_BOOT_SELECTED	0x02	/* bit 1 */
#define	IBFT_USE_RADIUS_CHAP		0x04	/* bit 2 */
#define	IBFT_USE_GLOBLE			0x04	/* NIC structure */
#define	IBFT_USE_RADIUS_RHCAP		0x08	/* bit 3 */

/*
 * Currently, we only support initiator offset, NIC0 offset, Target0 offset,
 * NIC1 offset and Target1 offset. So the length is 5. If we want to support
 * extensions, we should change this number.
 */
#define	IBFT_OFFSET_BUF_LEN		5
#define	IPV4_OFFSET			12

#define	IBFT_INVALID_MSG		"Invalid iBFT table 0x%x"
#define	IBFT_NOPROBE_MSG		"iSCSI boot is disabled"

typedef enum ibft_status {
	IBFT_STATUS_OK = 0,
	/* General error */
	IBFT_STATUS_ERR,
	/* Bad header */
	IBFT_STATUS_BADHDR,
	/* Bad control ID */
	IBFT_STATUS_BADCID,
	/* Bad ip addr */
	IBFT_STATUS_BADIP,
	/* Bad af */
	IBFT_STATUS_BADAF,
	/* Bad chap name */
	IBFT_STATUS_BADCHAPNAME,
	/* Bad chap secret */
	IBFT_STATUS_BADCHAPSEC,
	/* Bad checksum */
	IBFT_STATUS_BADCHECKSUM,
	/* Low memory */
	IBFT_STATUS_LOWMEM,
	/* No table */
	IBFT_STATUS_NOTABLE
} ibft_status_t;

extern void *memset(void *s, int c, size_t n);
extern int memcmp(const void *s1, const void *s2, size_t n);
extern void bcopy(const void *s1, void *s2, size_t n);
extern void iscsi_print_boot_property();

int ibft_noprobe = 0;
ib_boot_prop_t boot_property;		/* static allocated */
extern ib_boot_prop_t *iscsiboot_prop;	/* to be filled */

static ibft_status_t iscsi_parse_ibft_control(iscsi_ibft_ctl_t *ctl_hdr,
    ushort_t *iscsi_offset_buf);

static ibft_status_t iscsi_parse_ibft_initiator(char *begin_of_ibft,
    iscsi_ibft_initiator_t *initiator);

static ibft_status_t iscsi_parse_ibft_NIC(iscsi_ibft_nic_t *nicp);

static ibft_status_t iscsi_parse_ibft_target(char *begin_of_ibft,
    iscsi_ibft_tgt_t *tgtp);


/*
 * Return value:
 * Success: IBFT_STATUS_OK
 * Fail: IBFT_STATUS_BADCHECKSUM
 */
static ibft_status_t
iscsi_ibft_hdr_checksum(iscsi_ibft_tbl_hdr_t *tbl_hdr)
{
	uchar_t	checksum    =	0;
	uchar_t	*start	    =	NULL;
	int	length	    =	0;
	int	i	    =	0;

	if (tbl_hdr == NULL) {
		return (IBFT_STATUS_BADHDR);
	}

	length = tbl_hdr->Length;
	start = (uchar_t *)tbl_hdr;

	for (i = 0; i < length; i++) {
		checksum = checksum + start[i];
	}

	if (!checksum)
		return (IBFT_STATUS_OK);
	else
		return (IBFT_STATUS_BADCHECKSUM);
}

/*
 * Now we only support one control structure in the IBFT.
 * So there is no Control ID here.
 */
static ibft_status_t
iscsi_parse_ibft_structure(char *begin_of_ibft, char *buf)
{
	iscsi_ibft_hdr_t	*hdr	=   NULL;
	ibft_status_t		ret	=   IBFT_STATUS_OK;

	if (buf == NULL) {
		return (IBFT_STATUS_ERR);
	}

	hdr = (iscsi_ibft_hdr_t *)buf;
	switch (hdr->Structure_id) {
		case Initiator:
			ret = iscsi_parse_ibft_initiator(
			    begin_of_ibft,
			    (iscsi_ibft_initiator_t *)buf);
			break;
		case Nic:
			ret = iscsi_parse_ibft_NIC(
			    (iscsi_ibft_nic_t *)buf);
			break;
		case Target:
			ret = iscsi_parse_ibft_target(
			    begin_of_ibft,
			    (iscsi_ibft_tgt_t *)buf);
			break;
		default:
			ret = IBFT_STATUS_BADHDR;
			break;
	}

	return (ret);
}

/*
 * Parse the iBFT table
 * return IBFT_STATUS_OK upon sucess
 */
static ibft_status_t
iscsi_parse_ibft_tbl(iscsi_ibft_tbl_hdr_t *tbl_hdr)
{
	char		*outbuf	    =	NULL;
	int		i	    =	0;
	ibft_status_t	ret	    =	IBFT_STATUS_OK;
	ushort_t	iscsi_offset_buf[IBFT_OFFSET_BUF_LEN] = {0};

	if (tbl_hdr == NULL) {
		return (IBFT_STATUS_ERR);
	}

	if (iscsi_ibft_hdr_checksum(tbl_hdr) != IBFT_STATUS_OK) {
		return (IBFT_STATUS_BADCHECKSUM);
	}

	outbuf = (char *)tbl_hdr;

	ret = iscsi_parse_ibft_control(
	    (iscsi_ibft_ctl_t *)&outbuf[ISCSI_IBFT_CTL_OFFSET],
	    iscsi_offset_buf);

	if (ret == IBFT_STATUS_OK) {
		ret = IBFT_STATUS_ERR;
		for (i = 0; i < IBFT_OFFSET_BUF_LEN; i++) {
			if (iscsi_offset_buf[i] != 0) {
				ret = iscsi_parse_ibft_structure(
				    (char *)tbl_hdr,
				    (char *)tbl_hdr +
				    iscsi_offset_buf[i]);
				if (ret != IBFT_STATUS_OK) {
					return (ret);
				}
			}
		}
	}

	return (ret);
}

static ibft_status_t
iscsi_parse_ibft_control(iscsi_ibft_ctl_t *ctl_hdr,
    ushort_t	*iscsi_offset_buf)
{
	int	    i		=	0;
	ushort_t    *offsetp	=	NULL;

	if (ctl_hdr == NULL) {
		return (IBFT_STATUS_BADHDR);
	}

	if (ctl_hdr->header.Structure_id != Control) {
		return (IBFT_STATUS_BADCID);
	}

	/*
	 * Copy the offsets to offset buffer.
	 */
	for (offsetp = &(ctl_hdr->Initiator_offset); i < IBFT_OFFSET_BUF_LEN;
	    offsetp++) {
		iscsi_offset_buf[i++] = *offsetp;
	}

	return (IBFT_STATUS_OK);
}

/*
 * We only copy the "Firmare Boot Selseted" and valid initiator
 * to the boot property.
 */
static ibft_status_t
iscsi_parse_ibft_initiator(char *begin_of_ibft,
    iscsi_ibft_initiator_t *initiator)
{
	if (initiator == NULL) {
		return (IBFT_STATUS_ERR);
	}

	if (initiator->header.Structure_id != Initiator) {
		return (IBFT_STATUS_BADHDR);
	}

	if ((initiator->header.Flags & IBFT_FIRMWARE_BOOT_SELECTED) &&
	    (initiator->header.Flags & IBFT_BLOCK_VALID_YES)) {
		/*
		 * If the initiator name exists, we will copy it to our own
		 * property structure
		 */
		if (initiator->ini_name_len != 0) {
			boot_property.boot_init.ini_name =
			    (uchar_t *)kmem_zalloc(
			    initiator->ini_name_len + 1, KM_SLEEP);
			boot_property.boot_init.ini_name_len =
			    initiator->ini_name_len + 1;
			(void) snprintf(
			    (char *)boot_property.boot_init.ini_name,
			    initiator->ini_name_len + 1, "%s",
			    begin_of_ibft + initiator->ini_name_offset);
		}
	}
	return (IBFT_STATUS_OK);
}

static ibft_status_t
iscsi_parse_ipaddr(uchar_t *source, char *dest, int *af)
{
	int i = 0;

	if (source == NULL) {
		return (IBFT_STATUS_ERR);
	}

	if (source[0] == 0x00 && source[1] == 0x00 &&
	    source[2] == 0x00 && source[3] == 0x00 &&
	    source[4] == 0x00 && source[5] == 0x00 &&
	    source[6] == 0x00 && source[7] == 0x00 &&
	    source[8] == 0x00 && source[9] == 0x00 &&
	    (source[10] == 0xff) && (source[11] == 0xff)) {
		/*
		 * IPv4 address
		 */
		if (dest != NULL) {
			(void) sprintf(dest, "%d.%d.%d.%d",
			    source[12], source[13], source[14], source[15]);
		}
		if (af != NULL) {
			*af = AF_INET;
		}
	} else {
		if (dest != NULL) {
			for (i = 0; i < 14; i = i + 2) {
				(void) sprintf(dest, "%02x%02x:", source[i],
				    source[i+1]);
				dest = dest + 5;
			}
			(void) sprintf(dest, "%02x%02x",
			    source[i], source[i+1]);
		}
		if (af != NULL) {
			*af = AF_INET6;
		}
	}

	return (IBFT_STATUS_OK);
}

/*
 * Copy the ip address from ibft. If IPv4 is used, we should copy
 * the address from 12th byte.
 */
static ibft_status_t
iscsi_copy_ibft_ipaddr(uchar_t *source, void *dest, int *af)
{
	ibft_status_t	ret		=	IBFT_STATUS_OK;
	int		sin_family	=	0;

	if (source == NULL || dest == NULL) {
		return (IBFT_STATUS_ERR);
	}
	ret = iscsi_parse_ipaddr(source, NULL, &sin_family);
	if (ret != 0) {
		return (IBFT_STATUS_BADIP);
	}

	if (sin_family == AF_INET) {
		bcopy(source+IPV4_OFFSET, dest, sizeof (struct in_addr));
	} else if (sin_family == AF_INET6) {
		bcopy(source, dest, sizeof (struct in6_addr));
	} else {
		return (IBFT_STATUS_BADAF);
	}

	if (af != NULL) {
		*af = sin_family;
	}
	return (IBFT_STATUS_OK);
}

/*
 * Maybe there are multiply NICs are available. We only copy the
 * "Firmare Boot Selseted" and valid one to the boot property.
 */
static ibft_status_t
iscsi_parse_ibft_NIC(iscsi_ibft_nic_t *nicp)
{
	ibft_status_t	ret	=	IBFT_STATUS_OK;
	int		af	=	0;

	if (nicp == NULL) {
		return (IBFT_STATUS_ERR);
	}

	if (nicp->header.Structure_id != Nic) {
		return (IBFT_STATUS_ERR);
	}

	if ((nicp->header.Flags & IBFT_FIRMWARE_BOOT_SELECTED) &&
	    (nicp->header.Flags & IBFT_BLOCK_VALID_YES)) {
		ret = iscsi_copy_ibft_ipaddr(nicp->ip_addr,
		    &boot_property.boot_nic.nic_ip_u, &af);
		if (ret != IBFT_STATUS_OK) {
			return (ret);
		}

		boot_property.boot_nic.sin_family = af;

		ret = iscsi_copy_ibft_ipaddr(nicp->Gateway,
		    &boot_property.boot_nic.nic_gw_u, NULL);
		if (ret != IBFT_STATUS_OK) {
			return (ret);
		}

		ret = iscsi_copy_ibft_ipaddr(nicp->dhcp,
		    &boot_property.boot_nic.nic_dhcp_u, NULL);
		if (ret != IBFT_STATUS_OK) {
			return (ret);
		}

		bcopy(nicp->mac, boot_property.boot_nic.nic_mac, 6);
		boot_property.boot_nic.sub_mask_prefix =
		    nicp->Subnet_Mask_Prefix;
	}

	return (IBFT_STATUS_OK);
}

/*
 * Maybe there are multiply targets are available. We only copy the
 * "Firmare Boot Selseted" and valid one to the boot property.
 */
static ibft_status_t
iscsi_parse_ibft_target(char *begin_of_ibft, iscsi_ibft_tgt_t *tgtp)
{
	char		*tmp	=   NULL;
	int		af	=   0;
	ibft_status_t	ret	=   IBFT_STATUS_OK;

	if (tgtp == NULL) {
		return (IBFT_STATUS_ERR);
	}

	if (tgtp->header.Structure_id != Target) {
		return (IBFT_STATUS_BADHDR);
	}

	if ((tgtp->header.Flags & IBFT_FIRMWARE_BOOT_SELECTED) &&
	    (tgtp->header.Flags & IBFT_BLOCK_VALID_YES)) {
		/*
		 * Get Target Address
		 */
		ret = iscsi_copy_ibft_ipaddr(tgtp->ip_addr,
		    &boot_property.boot_tgt.tgt_ip_u, &af);
		if (ret != IBFT_STATUS_OK) {
			return (ret);
		}
		boot_property.boot_tgt.sin_family = af;
		/*
		 * Get Target Name
		 */
		if (tgtp->target_name_len != 0) {
			boot_property.boot_tgt.tgt_name =
			    (uchar_t *)kmem_zalloc(tgtp->target_name_len + 1,
			    KM_SLEEP);
			boot_property.boot_tgt.tgt_name_len =
			    tgtp->target_name_len + 1;
			(void) snprintf(
			    (char *)boot_property.boot_tgt.tgt_name,
			    tgtp->target_name_len + 1, "%s",
			    begin_of_ibft + tgtp->target_name_offset);
		} else {
			boot_property.boot_tgt.tgt_name = NULL;
		}

		/* Get Dest Port */
		boot_property.boot_tgt.tgt_port = tgtp->port;

		boot_property.boot_tgt.lun_online = 0;

		/*
		 * Get CHAP secret and name.
		 */
		if (tgtp->chap_type != NO_CHAP) {
			if (tgtp->chap_name_len != 0) {
				boot_property.boot_init.ini_chap_name =
				    (uchar_t *)kmem_zalloc(
				    tgtp->chap_name_len + 1,
				    KM_SLEEP);
				boot_property.boot_init.ini_chap_name_len =
				    tgtp->chap_name_len + 1;
				tmp = (char *)
				    boot_property.boot_init.ini_chap_name;
				(void) snprintf(
				    tmp,
				    tgtp->chap_name_len + 1, "%s",
				    begin_of_ibft + tgtp->chap_name_offset);
			} else {
				/*
				 * Just set NULL, initiator is able to deal
				 * with this
				 */
				boot_property.boot_init.ini_chap_name = NULL;
			}

			if (tgtp->chap_secret_len != 0) {
				boot_property.boot_init.ini_chap_sec =
				    (uchar_t *)kmem_zalloc(
				    tgtp->chap_secret_len + 1,
				    KM_SLEEP);
				boot_property.boot_init.ini_chap_sec_len =
				    tgtp->chap_secret_len + 1;
				bcopy(begin_of_ibft +
				    tgtp->chap_secret_offset,
				    boot_property.boot_init.ini_chap_sec,
				    tgtp->chap_secret_len);
			} else {
				boot_property.boot_init.ini_chap_sec = NULL;
				return (IBFT_STATUS_ERR);
			}

			if (tgtp->chap_type == Mutual_CHAP) {
				if (tgtp->rev_chap_name_len != 0) {
					boot_property.boot_tgt.tgt_chap_name =
					    (uchar_t *)kmem_zalloc(
					    tgtp->rev_chap_name_len + 1,
					    KM_SLEEP);
					boot_property.boot_tgt.tgt_chap_name_len
					    = tgtp->rev_chap_name_len + 1;
#define	TGT_CHAP_NAME	boot_property.boot_tgt.tgt_chap_name
					tmp = (char *)TGT_CHAP_NAME;
#undef	TGT_CHAP_NAME
					(void) snprintf(
					    tmp,
					    tgtp->rev_chap_name_len + 1,
					    "%s",
					    begin_of_ibft +
					    tgtp->rev_chap_name_offset);
				} else {
					/*
					 * Just set NULL, initiator is able
					 * to deal with this
					 */
					boot_property.boot_tgt.tgt_chap_name =
					    NULL;
				}

				if (tgtp->rev_chap_secret_len != 0) {
					boot_property.boot_tgt.tgt_chap_sec =
					    (uchar_t *)kmem_zalloc(
					    tgtp->rev_chap_secret_len + 1,
					    KM_SLEEP);
					boot_property.boot_tgt.tgt_chap_sec_len
					    = tgtp->rev_chap_secret_len + 1;
					tmp = (char *)
					    boot_property.boot_tgt.tgt_chap_sec;
					(void) snprintf(
					    tmp,
					    tgtp->rev_chap_secret_len + 1,
					    "%s",
					    begin_of_ibft +
					    tgtp->chap_secret_offset);
				} else {
					boot_property.boot_tgt.tgt_chap_sec =
					    NULL;
					return (IBFT_STATUS_BADCHAPSEC);
				}
			}
		} else {
			boot_property.boot_init.ini_chap_name = NULL;
			boot_property.boot_init.ini_chap_sec = NULL;
		}

		/*
		 * Get Boot LUN
		 */
		(void) bcopy(tgtp->boot_lun,
		    boot_property.boot_tgt.tgt_boot_lun, 8);
	}

	return (IBFT_STATUS_OK);
}

/*
 * This function is used for scanning iBFT from the physical memory.
 * Return Value:
 * IBFT_STATUS_OK
 * IBFT_STATUS_ERR
 */
static ibft_status_t
iscsi_scan_ibft_tbl(char *ibft_tbl_buf)
{
	int		start;
	void		*va		= NULL;
	int		*len 		= NULL;
	ibft_status_t	ret		= IBFT_STATUS_NOTABLE;

	for (start = ISCSI_IBFT_LOWER_ADDR; start < ISCSI_IBFT_HIGHER_ADDR;
	    start = start + ISCSI_IBFT_ALIGNED) {
		va = (void *)psm_map((paddr_t)(start&0xffffffff),
		    ISCSI_IBFT_SIGNATURE_LEN,
		    PROT_READ);

		if (va == NULL) {
			continue;
		}
		if (memcmp(va, ISCSI_IBFT_SIGNATRUE,
		    ISCSI_IBFT_SIGNATURE_LEN) == 0) {
			ret = IBFT_STATUS_ERR;
			/* Acquire table length */
			len = (int *)psm_map(
			    (paddr_t)((start+\
			    ISCSI_IBFT_SIGNATURE_LEN)&0xffffffff),
			    ISCSI_IBFT_SIGNATURE_LEN, PROT_READ);
			if (len == NULL) {
				psm_unmap((caddr_t)va,
				    ISCSI_IBFT_SIGNATURE_LEN);
				continue;
			}
			if (ISCSI_IBFT_LOWER_ADDR + *len <
			    ISCSI_IBFT_HIGHER_ADDR - 1) {
				psm_unmap(va,
				    ISCSI_IBFT_SIGNATURE_LEN);
				va = psm_map((paddr_t)(start&0xffffffff),
				    *len,
				    PROT_READ);
				if (va != NULL) {
					/*
					 * Copy data to our own buffer
					 */
					bcopy(va, ibft_tbl_buf, *len);
					ret = IBFT_STATUS_OK;
				}
				psm_unmap((caddr_t)va, *len);
				psm_unmap((caddr_t)len,
				    ISCSI_IBFT_SIGNATURE_LEN);
				break;
			} else {
				psm_unmap((caddr_t)va,
				    ISCSI_IBFT_SIGNATURE_LEN);
				psm_unmap((caddr_t)len,
				    ISCSI_IBFT_SIGNATURE_LEN);
			}
		} else {
			psm_unmap((caddr_t)va, ISCSI_IBFT_SIGNATURE_LEN);
		}
	}

	return (ret);
}

/*
 * Scan the ibft table and store the iSCSI boot properties
 * If there is a valid table then set the iscsiboot_prop
 * iBF should be off if the host is not intended
 * to be booted from iSCSI disk
 */
void
ld_ib_prop()
{
	ibft_status_t	ret	=   IBFT_STATUS_OK;
	char		*ibft_tbl_buf;

	if (do_bsys_getproplen(NULL, "ibft-noprobe") > 0)
		ibft_noprobe = 1;

	if (ibft_noprobe != 0) {
		/*
		 * Scanning for iBFT may conflict with devices which use memory
		 * in 640-1024KB of physical address space.  The iBFT
		 * specification suggests use of low RAM method - scanning
		 * physical memory 512-1024 KB for iBFT table.  However, the
		 * Upper Memory Area (UMA) 640-1024 KB may contain device
		 * memory or memory mapped I/O.  Although reading from I/O area
		 * is usually fine, the actual behavior depends on device
		 * implementation.  In some cases, the user may want to disable
		 * low RAM method and prevent reading from device I/O area.
		 *
		 * To disable low RAM method:
		 * 1) pass "-B ibft-noprobe=1" on kernel command line
		 * 2) add line "set ibft_noprobe=1" in /etc/system
		 */
		cmn_err(CE_NOTE, IBFT_NOPROBE_MSG);
		return;
	}

	ibft_tbl_buf = (char *)kmem_zalloc(ISCSI_IBFT_TBL_BUF_LEN,
	    KM_SLEEP);

	if (!ibft_tbl_buf) {
		/* Unlikely to happen */
		cmn_err(CE_NOTE, IBFT_INVALID_MSG,
		    IBFT_STATUS_LOWMEM);
		return;
	}

	(void) memset(&boot_property, 0, sizeof (boot_property));
	if ((ret = iscsi_scan_ibft_tbl(ibft_tbl_buf)) ==
	    IBFT_STATUS_OK) {
		ret = iscsi_parse_ibft_tbl(
		    (iscsi_ibft_tbl_hdr_t *)ibft_tbl_buf);
		if (ret == IBFT_STATUS_OK) {
			iscsiboot_prop = &boot_property;
			iscsi_print_boot_property();
		} else {
			cmn_err(CE_NOTE, IBFT_INVALID_MSG, ret);
		}
	} else if (ret != IBFT_STATUS_NOTABLE) {
		cmn_err(CE_NOTE, IBFT_INVALID_MSG, ret);
	}

	kmem_free(ibft_tbl_buf, ISCSI_IBFT_TBL_BUF_LEN);
}