#include <sys/types.h>
#include <sys/null.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>
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
#define ISCSI_IBFT_HIGHER_ADDR 0x100000
#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
#define IBFT_FIRMWARE_BOOT_SELECTED 0x02
#define IBFT_USE_RADIUS_CHAP 0x04
#define IBFT_USE_GLOBLE 0x04
#define IBFT_USE_RADIUS_RHCAP 0x08
#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,
IBFT_STATUS_ERR,
IBFT_STATUS_BADHDR,
IBFT_STATUS_BADCID,
IBFT_STATUS_BADIP,
IBFT_STATUS_BADAF,
IBFT_STATUS_BADCHAPNAME,
IBFT_STATUS_BADCHAPSEC,
IBFT_STATUS_BADCHECKSUM,
IBFT_STATUS_LOWMEM,
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;
extern ib_boot_prop_t *iscsiboot_prop;
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);
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);
}
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);
}
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);
}
for (offsetp = &(ctl_hdr->Initiator_offset); i < IBFT_OFFSET_BUF_LEN;
offsetp++) {
iscsi_offset_buf[i++] = *offsetp;
}
return (IBFT_STATUS_OK);
}
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 (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)) {
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);
}
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);
}
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);
}
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)) {
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;
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;
}
boot_property.boot_tgt.tgt_port = tgtp->port;
boot_property.boot_tgt.lun_online = 0;
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 {
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 {
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;
}
(void) bcopy(tgtp->boot_lun,
boot_property.boot_tgt.tgt_boot_lun, 8);
}
return (IBFT_STATUS_OK);
}
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;
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) {
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);
}
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) {
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) {
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);
}