4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * This is the place to implement ld_ib_props()
29 * For x86 it is to load iBFT and costruct the global ib props
32 #include <sys/types.h>
33 #include <sys/cmn_err.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
37 #include <sys/bootprops.h>
40 #include <sys/bootconf.h>
46 typedef enum ibft_structure_type
{
56 typedef enum _chap_type
{
63 typedef struct ibft_entry
{
66 char target_name
[224];
67 char target_addr
[INET6_ADDRSTRLEN
];
70 typedef struct iSCSI_ibft_tbl_hdr
{
78 }iscsi_ibft_tbl_hdr_t
;
80 typedef struct iSCSI_ibft_hdr
{
88 typedef struct iSCSI_ibft_control
{
89 iscsi_ibft_hdr_t header
;
91 ushort_t Initiator_offset
;
93 ushort_t Target0_offset
;
95 ushort_t Target1_offset
;
98 typedef struct iSCSI_ibft_initiator
{
99 iscsi_ibft_hdr_t header
;
100 uchar_t iSNS_Server
[16];
101 uchar_t SLP_Server
[16];
102 uchar_t Pri_Radius_Server
[16];
103 uchar_t Sec_Radius_Server
[16];
104 ushort_t ini_name_len
;
105 ushort_t ini_name_offset
;
106 }iscsi_ibft_initiator_t
;
108 typedef struct iSCSI_ibft_nic
{
109 iscsi_ibft_hdr_t header
;
111 char Subnet_Mask_Prefix
;
114 uchar_t Primary_dns
[16];
115 uchar_t Secondary_dns
[16];
120 ushort_t Hostname_len
;
121 ushort_t Hostname_offset
;
124 typedef struct iSCSI_ibft_target
{
125 iscsi_ibft_hdr_t header
;
130 uchar_t nic_association
;
131 ushort_t target_name_len
;
132 ushort_t target_name_offset
;
133 ushort_t chap_name_len
;
134 ushort_t chap_name_offset
;
135 ushort_t chap_secret_len
;
136 ushort_t chap_secret_offset
;
137 ushort_t rev_chap_name_len
;
138 ushort_t rev_chap_name_offset
;
139 ushort_t rev_chap_secret_len
;
140 ushort_t rev_chap_secret_offset
;
143 #define ISCSI_IBFT_LOWER_ADDR 0x80000 /* 512K */
144 #define ISCSI_IBFT_HIGHER_ADDR 0x100000 /* 1024K */
145 #define ISCSI_IBFT_SIGNATRUE "iBFT"
146 #define ISCSI_IBFT_SIGNATURE_LEN 4
147 #define ISCSI_IBFT_TBL_BUF_LEN 1024
148 #define ISCSI_IBFT_ALIGNED 16
149 #define ISCSI_IBFT_CTL_OFFSET 48
151 #define IBFT_BLOCK_VALID_YES 0x01 /* bit 0 */
152 #define IBFT_FIRMWARE_BOOT_SELECTED 0x02 /* bit 1 */
153 #define IBFT_USE_RADIUS_CHAP 0x04 /* bit 2 */
154 #define IBFT_USE_GLOBLE 0x04 /* NIC structure */
155 #define IBFT_USE_RADIUS_RHCAP 0x08 /* bit 3 */
158 * Currently, we only support initiator offset, NIC0 offset, Target0 offset,
159 * NIC1 offset and Target1 offset. So the length is 5. If we want to support
160 * extensions, we should change this number.
162 #define IBFT_OFFSET_BUF_LEN 5
163 #define IPV4_OFFSET 12
165 #define IBFT_INVALID_MSG "Invalid iBFT table 0x%x"
166 #define IBFT_NOPROBE_MSG "iSCSI boot is disabled"
168 typedef enum ibft_status
{
181 IBFT_STATUS_BADCHAPNAME
,
182 /* Bad chap secret */
183 IBFT_STATUS_BADCHAPSEC
,
185 IBFT_STATUS_BADCHECKSUM
,
192 extern void *memset(void *s
, int c
, size_t n
);
193 extern int memcmp(const void *s1
, const void *s2
, size_t n
);
194 extern void bcopy(const void *s1
, void *s2
, size_t n
);
195 extern void iscsi_print_boot_property();
197 int ibft_noprobe
= 0;
198 ib_boot_prop_t boot_property
; /* static allocated */
199 extern ib_boot_prop_t
*iscsiboot_prop
; /* to be filled */
201 static ibft_status_t
iscsi_parse_ibft_control(iscsi_ibft_ctl_t
*ctl_hdr
,
202 ushort_t
*iscsi_offset_buf
);
204 static ibft_status_t
iscsi_parse_ibft_initiator(char *begin_of_ibft
,
205 iscsi_ibft_initiator_t
*initiator
);
207 static ibft_status_t
iscsi_parse_ibft_NIC(iscsi_ibft_nic_t
*nicp
);
209 static ibft_status_t
iscsi_parse_ibft_target(char *begin_of_ibft
,
210 iscsi_ibft_tgt_t
*tgtp
);
215 * Success: IBFT_STATUS_OK
216 * Fail: IBFT_STATUS_BADCHECKSUM
219 iscsi_ibft_hdr_checksum(iscsi_ibft_tbl_hdr_t
*tbl_hdr
)
221 uchar_t checksum
= 0;
222 uchar_t
*start
= NULL
;
226 if (tbl_hdr
== NULL
) {
227 return (IBFT_STATUS_BADHDR
);
230 length
= tbl_hdr
->Length
;
231 start
= (uchar_t
*)tbl_hdr
;
233 for (i
= 0; i
< length
; i
++) {
234 checksum
= checksum
+ start
[i
];
238 return (IBFT_STATUS_OK
);
240 return (IBFT_STATUS_BADCHECKSUM
);
244 * Now we only support one control structure in the IBFT.
245 * So there is no Control ID here.
248 iscsi_parse_ibft_structure(char *begin_of_ibft
, char *buf
)
250 iscsi_ibft_hdr_t
*hdr
= NULL
;
251 ibft_status_t ret
= IBFT_STATUS_OK
;
254 return (IBFT_STATUS_ERR
);
257 hdr
= (iscsi_ibft_hdr_t
*)buf
;
258 switch (hdr
->Structure_id
) {
260 ret
= iscsi_parse_ibft_initiator(
262 (iscsi_ibft_initiator_t
*)buf
);
265 ret
= iscsi_parse_ibft_NIC(
266 (iscsi_ibft_nic_t
*)buf
);
269 ret
= iscsi_parse_ibft_target(
271 (iscsi_ibft_tgt_t
*)buf
);
274 ret
= IBFT_STATUS_BADHDR
;
282 * Parse the iBFT table
283 * return IBFT_STATUS_OK upon sucess
286 iscsi_parse_ibft_tbl(iscsi_ibft_tbl_hdr_t
*tbl_hdr
)
290 ibft_status_t ret
= IBFT_STATUS_OK
;
291 ushort_t iscsi_offset_buf
[IBFT_OFFSET_BUF_LEN
] = {0};
293 if (tbl_hdr
== NULL
) {
294 return (IBFT_STATUS_ERR
);
297 if (iscsi_ibft_hdr_checksum(tbl_hdr
) != IBFT_STATUS_OK
) {
298 return (IBFT_STATUS_BADCHECKSUM
);
301 outbuf
= (char *)tbl_hdr
;
303 ret
= iscsi_parse_ibft_control(
304 (iscsi_ibft_ctl_t
*)&outbuf
[ISCSI_IBFT_CTL_OFFSET
],
307 if (ret
== IBFT_STATUS_OK
) {
308 ret
= IBFT_STATUS_ERR
;
309 for (i
= 0; i
< IBFT_OFFSET_BUF_LEN
; i
++) {
310 if (iscsi_offset_buf
[i
] != 0) {
311 ret
= iscsi_parse_ibft_structure(
314 iscsi_offset_buf
[i
]);
315 if (ret
!= IBFT_STATUS_OK
) {
326 iscsi_parse_ibft_control(iscsi_ibft_ctl_t
*ctl_hdr
,
327 ushort_t
*iscsi_offset_buf
)
330 ushort_t
*offsetp
= NULL
;
332 if (ctl_hdr
== NULL
) {
333 return (IBFT_STATUS_BADHDR
);
336 if (ctl_hdr
->header
.Structure_id
!= Control
) {
337 return (IBFT_STATUS_BADCID
);
341 * Copy the offsets to offset buffer.
343 for (offsetp
= &(ctl_hdr
->Initiator_offset
); i
< IBFT_OFFSET_BUF_LEN
;
345 iscsi_offset_buf
[i
++] = *offsetp
;
348 return (IBFT_STATUS_OK
);
352 * We only copy the "Firmare Boot Selseted" and valid initiator
353 * to the boot property.
356 iscsi_parse_ibft_initiator(char *begin_of_ibft
,
357 iscsi_ibft_initiator_t
*initiator
)
359 if (initiator
== NULL
) {
360 return (IBFT_STATUS_ERR
);
363 if (initiator
->header
.Structure_id
!= Initiator
) {
364 return (IBFT_STATUS_BADHDR
);
367 if ((initiator
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
368 (initiator
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
370 * If the initiator name exists, we will copy it to our own
373 if (initiator
->ini_name_len
!= 0) {
374 boot_property
.boot_init
.ini_name
=
375 (uchar_t
*)kmem_zalloc(
376 initiator
->ini_name_len
+ 1, KM_SLEEP
);
377 boot_property
.boot_init
.ini_name_len
=
378 initiator
->ini_name_len
+ 1;
380 (char *)boot_property
.boot_init
.ini_name
,
381 initiator
->ini_name_len
+ 1, "%s",
382 begin_of_ibft
+ initiator
->ini_name_offset
);
385 return (IBFT_STATUS_OK
);
389 iscsi_parse_ipaddr(uchar_t
*source
, char *dest
, int *af
)
393 if (source
== NULL
) {
394 return (IBFT_STATUS_ERR
);
397 if (source
[0] == 0x00 && source
[1] == 0x00 &&
398 source
[2] == 0x00 && source
[3] == 0x00 &&
399 source
[4] == 0x00 && source
[5] == 0x00 &&
400 source
[6] == 0x00 && source
[7] == 0x00 &&
401 source
[8] == 0x00 && source
[9] == 0x00 &&
402 (source
[10] == 0xff) && (source
[11] == 0xff)) {
407 (void) sprintf(dest
, "%d.%d.%d.%d",
408 source
[12], source
[13], source
[14], source
[15]);
415 for (i
= 0; i
< 14; i
= i
+ 2) {
416 (void) sprintf(dest
, "%02x%02x:", source
[i
],
420 (void) sprintf(dest
, "%02x%02x",
421 source
[i
], source
[i
+1]);
428 return (IBFT_STATUS_OK
);
432 * Copy the ip address from ibft. If IPv4 is used, we should copy
433 * the address from 12th byte.
436 iscsi_copy_ibft_ipaddr(uchar_t
*source
, void *dest
, int *af
)
438 ibft_status_t ret
= IBFT_STATUS_OK
;
441 if (source
== NULL
|| dest
== NULL
) {
442 return (IBFT_STATUS_ERR
);
444 ret
= iscsi_parse_ipaddr(source
, NULL
, &sin_family
);
446 return (IBFT_STATUS_BADIP
);
449 if (sin_family
== AF_INET
) {
450 bcopy(source
+IPV4_OFFSET
, dest
, sizeof (struct in_addr
));
451 } else if (sin_family
== AF_INET6
) {
452 bcopy(source
, dest
, sizeof (struct in6_addr
));
454 return (IBFT_STATUS_BADAF
);
460 return (IBFT_STATUS_OK
);
464 * Maybe there are multiply NICs are available. We only copy the
465 * "Firmare Boot Selseted" and valid one to the boot property.
468 iscsi_parse_ibft_NIC(iscsi_ibft_nic_t
*nicp
)
470 ibft_status_t ret
= IBFT_STATUS_OK
;
474 return (IBFT_STATUS_ERR
);
477 if (nicp
->header
.Structure_id
!= Nic
) {
478 return (IBFT_STATUS_ERR
);
481 if ((nicp
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
482 (nicp
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
483 ret
= iscsi_copy_ibft_ipaddr(nicp
->ip_addr
,
484 &boot_property
.boot_nic
.nic_ip_u
, &af
);
485 if (ret
!= IBFT_STATUS_OK
) {
489 boot_property
.boot_nic
.sin_family
= af
;
491 ret
= iscsi_copy_ibft_ipaddr(nicp
->Gateway
,
492 &boot_property
.boot_nic
.nic_gw_u
, NULL
);
493 if (ret
!= IBFT_STATUS_OK
) {
497 ret
= iscsi_copy_ibft_ipaddr(nicp
->dhcp
,
498 &boot_property
.boot_nic
.nic_dhcp_u
, NULL
);
499 if (ret
!= IBFT_STATUS_OK
) {
503 bcopy(nicp
->mac
, boot_property
.boot_nic
.nic_mac
, 6);
504 boot_property
.boot_nic
.sub_mask_prefix
=
505 nicp
->Subnet_Mask_Prefix
;
508 return (IBFT_STATUS_OK
);
512 * Maybe there are multiply targets are available. We only copy the
513 * "Firmare Boot Selseted" and valid one to the boot property.
516 iscsi_parse_ibft_target(char *begin_of_ibft
, iscsi_ibft_tgt_t
*tgtp
)
520 ibft_status_t ret
= IBFT_STATUS_OK
;
523 return (IBFT_STATUS_ERR
);
526 if (tgtp
->header
.Structure_id
!= Target
) {
527 return (IBFT_STATUS_BADHDR
);
530 if ((tgtp
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
531 (tgtp
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
535 ret
= iscsi_copy_ibft_ipaddr(tgtp
->ip_addr
,
536 &boot_property
.boot_tgt
.tgt_ip_u
, &af
);
537 if (ret
!= IBFT_STATUS_OK
) {
540 boot_property
.boot_tgt
.sin_family
= af
;
544 if (tgtp
->target_name_len
!= 0) {
545 boot_property
.boot_tgt
.tgt_name
=
546 (uchar_t
*)kmem_zalloc(tgtp
->target_name_len
+ 1,
548 boot_property
.boot_tgt
.tgt_name_len
=
549 tgtp
->target_name_len
+ 1;
551 (char *)boot_property
.boot_tgt
.tgt_name
,
552 tgtp
->target_name_len
+ 1, "%s",
553 begin_of_ibft
+ tgtp
->target_name_offset
);
555 boot_property
.boot_tgt
.tgt_name
= NULL
;
559 boot_property
.boot_tgt
.tgt_port
= tgtp
->port
;
561 boot_property
.boot_tgt
.lun_online
= 0;
564 * Get CHAP secret and name.
566 if (tgtp
->chap_type
!= NO_CHAP
) {
567 if (tgtp
->chap_name_len
!= 0) {
568 boot_property
.boot_init
.ini_chap_name
=
569 (uchar_t
*)kmem_zalloc(
570 tgtp
->chap_name_len
+ 1,
572 boot_property
.boot_init
.ini_chap_name_len
=
573 tgtp
->chap_name_len
+ 1;
575 boot_property
.boot_init
.ini_chap_name
;
578 tgtp
->chap_name_len
+ 1, "%s",
579 begin_of_ibft
+ tgtp
->chap_name_offset
);
582 * Just set NULL, initiator is able to deal
585 boot_property
.boot_init
.ini_chap_name
= NULL
;
588 if (tgtp
->chap_secret_len
!= 0) {
589 boot_property
.boot_init
.ini_chap_sec
=
590 (uchar_t
*)kmem_zalloc(
591 tgtp
->chap_secret_len
+ 1,
593 boot_property
.boot_init
.ini_chap_sec_len
=
594 tgtp
->chap_secret_len
+ 1;
595 bcopy(begin_of_ibft
+
596 tgtp
->chap_secret_offset
,
597 boot_property
.boot_init
.ini_chap_sec
,
598 tgtp
->chap_secret_len
);
600 boot_property
.boot_init
.ini_chap_sec
= NULL
;
601 return (IBFT_STATUS_ERR
);
604 if (tgtp
->chap_type
== Mutual_CHAP
) {
605 if (tgtp
->rev_chap_name_len
!= 0) {
606 boot_property
.boot_tgt
.tgt_chap_name
=
607 (uchar_t
*)kmem_zalloc(
608 tgtp
->rev_chap_name_len
+ 1,
610 boot_property
.boot_tgt
.tgt_chap_name_len
611 = tgtp
->rev_chap_name_len
+ 1;
612 #define TGT_CHAP_NAME boot_property.boot_tgt.tgt_chap_name
613 tmp
= (char *)TGT_CHAP_NAME
;
617 tgtp
->rev_chap_name_len
+ 1,
620 tgtp
->rev_chap_name_offset
);
623 * Just set NULL, initiator is able
626 boot_property
.boot_tgt
.tgt_chap_name
=
630 if (tgtp
->rev_chap_secret_len
!= 0) {
631 boot_property
.boot_tgt
.tgt_chap_sec
=
632 (uchar_t
*)kmem_zalloc(
633 tgtp
->rev_chap_secret_len
+ 1,
635 boot_property
.boot_tgt
.tgt_chap_sec_len
636 = tgtp
->rev_chap_secret_len
+ 1;
638 boot_property
.boot_tgt
.tgt_chap_sec
;
641 tgtp
->rev_chap_secret_len
+ 1,
644 tgtp
->chap_secret_offset
);
646 boot_property
.boot_tgt
.tgt_chap_sec
=
648 return (IBFT_STATUS_BADCHAPSEC
);
652 boot_property
.boot_init
.ini_chap_name
= NULL
;
653 boot_property
.boot_init
.ini_chap_sec
= NULL
;
659 (void) bcopy(tgtp
->boot_lun
,
660 boot_property
.boot_tgt
.tgt_boot_lun
, 8);
663 return (IBFT_STATUS_OK
);
667 * This function is used for scanning iBFT from the physical memory.
673 iscsi_scan_ibft_tbl(char *ibft_tbl_buf
)
678 ibft_status_t ret
= IBFT_STATUS_NOTABLE
;
680 for (start
= ISCSI_IBFT_LOWER_ADDR
; start
< ISCSI_IBFT_HIGHER_ADDR
;
681 start
= start
+ ISCSI_IBFT_ALIGNED
) {
682 va
= (void *)psm_map((paddr_t
)(start
&0xffffffff),
683 ISCSI_IBFT_SIGNATURE_LEN
,
689 if (memcmp(va
, ISCSI_IBFT_SIGNATRUE
,
690 ISCSI_IBFT_SIGNATURE_LEN
) == 0) {
691 ret
= IBFT_STATUS_ERR
;
692 /* Acquire table length */
693 len
= (int *)psm_map(
695 ISCSI_IBFT_SIGNATURE_LEN
)&0xffffffff),
696 ISCSI_IBFT_SIGNATURE_LEN
, PROT_READ
);
698 psm_unmap((caddr_t
)va
,
699 ISCSI_IBFT_SIGNATURE_LEN
);
702 if (ISCSI_IBFT_LOWER_ADDR
+ *len
<
703 ISCSI_IBFT_HIGHER_ADDR
- 1) {
705 ISCSI_IBFT_SIGNATURE_LEN
);
706 va
= psm_map((paddr_t
)(start
&0xffffffff),
711 * Copy data to our own buffer
713 bcopy(va
, ibft_tbl_buf
, *len
);
714 ret
= IBFT_STATUS_OK
;
716 psm_unmap((caddr_t
)va
, *len
);
717 psm_unmap((caddr_t
)len
,
718 ISCSI_IBFT_SIGNATURE_LEN
);
721 psm_unmap((caddr_t
)va
,
722 ISCSI_IBFT_SIGNATURE_LEN
);
723 psm_unmap((caddr_t
)len
,
724 ISCSI_IBFT_SIGNATURE_LEN
);
727 psm_unmap((caddr_t
)va
, ISCSI_IBFT_SIGNATURE_LEN
);
735 * Scan the ibft table and store the iSCSI boot properties
736 * If there is a valid table then set the iscsiboot_prop
737 * iBF should be off if the host is not intended
738 * to be booted from iSCSI disk
743 ibft_status_t ret
= IBFT_STATUS_OK
;
746 if (do_bsys_getproplen(NULL
, "ibft-noprobe") > 0)
749 if (ibft_noprobe
!= 0) {
751 * Scanning for iBFT may conflict with devices which use memory
752 * in 640-1024KB of physical address space. The iBFT
753 * specification suggests use of low RAM method - scanning
754 * physical memory 512-1024 KB for iBFT table. However, the
755 * Upper Memory Area (UMA) 640-1024 KB may contain device
756 * memory or memory mapped I/O. Although reading from I/O area
757 * is usually fine, the actual behavior depends on device
758 * implementation. In some cases, the user may want to disable
759 * low RAM method and prevent reading from device I/O area.
761 * To disable low RAM method:
762 * 1) pass "-B ibft-noprobe=1" on kernel command line
763 * 2) add line "set ibft_noprobe=1" in /etc/system
765 cmn_err(CE_NOTE
, IBFT_NOPROBE_MSG
);
769 ibft_tbl_buf
= (char *)kmem_zalloc(ISCSI_IBFT_TBL_BUF_LEN
,
773 /* Unlikely to happen */
774 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
,
779 (void) memset(&boot_property
, 0, sizeof (boot_property
));
780 if ((ret
= iscsi_scan_ibft_tbl(ibft_tbl_buf
)) ==
782 ret
= iscsi_parse_ibft_tbl(
783 (iscsi_ibft_tbl_hdr_t
*)ibft_tbl_buf
);
784 if (ret
== IBFT_STATUS_OK
) {
785 iscsiboot_prop
= &boot_property
;
786 iscsi_print_boot_property();
788 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
, ret
);
790 } else if (ret
!= IBFT_STATUS_NOTABLE
) {
791 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
, ret
);
794 kmem_free(ibft_tbl_buf
, ISCSI_IBFT_TBL_BUF_LEN
);