diff options
Diffstat (limited to 'usr/src/uts/i86pc/os/ibft.c')
| -rw-r--r-- | usr/src/uts/i86pc/os/ibft.c | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/usr/src/uts/i86pc/os/ibft.c b/usr/src/uts/i86pc/os/ibft.c new file mode 100644 index 0000000000..141b837988 --- /dev/null +++ b/usr/src/uts/i86pc/os/ibft.c @@ -0,0 +1,757 @@ +/* + * 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 2008 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> + +#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" + +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(); + +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); + (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); + (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); + 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); + 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->chap_name_len + 1, + KM_SLEEP); +#define TGT_CHAP_NAME boot_property.boot_tgt.tgt_chap_name + tmp = (char *)TGT_CHAP_NAME; +#undef TGT_CHAP_NAME + (void) snprintf( + tmp, + tgtp->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); + 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; + + 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); +} |
