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>
34 #include <sys/cmn_err.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
38 #include <sys/bootprops.h>
41 #include <sys/bootconf.h>
43 typedef enum ibft_structure_type
{
53 typedef enum _chap_type
{
60 typedef struct ibft_entry
{
63 char target_name
[224];
64 char target_addr
[INET6_ADDRSTRLEN
];
67 typedef struct iSCSI_ibft_tbl_hdr
{
75 }iscsi_ibft_tbl_hdr_t
;
77 typedef struct iSCSI_ibft_hdr
{
85 typedef struct iSCSI_ibft_control
{
86 iscsi_ibft_hdr_t header
;
88 ushort_t Initiator_offset
;
90 ushort_t Target0_offset
;
92 ushort_t Target1_offset
;
95 typedef struct iSCSI_ibft_initiator
{
96 iscsi_ibft_hdr_t header
;
97 uchar_t iSNS_Server
[16];
98 uchar_t SLP_Server
[16];
99 uchar_t Pri_Radius_Server
[16];
100 uchar_t Sec_Radius_Server
[16];
101 ushort_t ini_name_len
;
102 ushort_t ini_name_offset
;
103 }iscsi_ibft_initiator_t
;
105 typedef struct iSCSI_ibft_nic
{
106 iscsi_ibft_hdr_t header
;
108 char Subnet_Mask_Prefix
;
111 uchar_t Primary_dns
[16];
112 uchar_t Secondary_dns
[16];
117 ushort_t Hostname_len
;
118 ushort_t Hostname_offset
;
121 typedef struct iSCSI_ibft_target
{
122 iscsi_ibft_hdr_t header
;
127 uchar_t nic_association
;
128 ushort_t target_name_len
;
129 ushort_t target_name_offset
;
130 ushort_t chap_name_len
;
131 ushort_t chap_name_offset
;
132 ushort_t chap_secret_len
;
133 ushort_t chap_secret_offset
;
134 ushort_t rev_chap_name_len
;
135 ushort_t rev_chap_name_offset
;
136 ushort_t rev_chap_secret_len
;
137 ushort_t rev_chap_secret_offset
;
140 #define ISCSI_IBFT_LOWER_ADDR 0x80000 /* 512K */
141 #define ISCSI_IBFT_HIGHER_ADDR 0x100000 /* 1024K */
142 #define ISCSI_IBFT_SIGNATRUE "iBFT"
143 #define ISCSI_IBFT_SIGNATURE_LEN 4
144 #define ISCSI_IBFT_TBL_BUF_LEN 1024
145 #define ISCSI_IBFT_ALIGNED 16
146 #define ISCSI_IBFT_CTL_OFFSET 48
148 #define IBFT_BLOCK_VALID_YES 0x01 /* bit 0 */
149 #define IBFT_FIRMWARE_BOOT_SELECTED 0x02 /* bit 1 */
150 #define IBFT_USE_RADIUS_CHAP 0x04 /* bit 2 */
151 #define IBFT_USE_GLOBLE 0x04 /* NIC structure */
152 #define IBFT_USE_RADIUS_RHCAP 0x08 /* bit 3 */
155 * Currently, we only support initiator offset, NIC0 offset, Target0 offset,
156 * NIC1 offset and Target1 offset. So the length is 5. If we want to support
157 * extensions, we should change this number.
159 #define IBFT_OFFSET_BUF_LEN 5
160 #define IPV4_OFFSET 12
162 #define IBFT_INVALID_MSG "Invalid iBFT table 0x%x"
163 #define IBFT_NOPROBE_MSG "iSCSI boot is disabled"
165 typedef enum ibft_status
{
178 IBFT_STATUS_BADCHAPNAME
,
179 /* Bad chap secret */
180 IBFT_STATUS_BADCHAPSEC
,
182 IBFT_STATUS_BADCHECKSUM
,
189 extern void *memset(void *s
, int c
, size_t n
);
190 extern int memcmp(const void *s1
, const void *s2
, size_t n
);
191 extern void bcopy(const void *s1
, void *s2
, size_t n
);
192 extern void iscsi_print_boot_property();
194 int ibft_noprobe
= 0;
195 ib_boot_prop_t boot_property
; /* static allocated */
196 extern ib_boot_prop_t
*iscsiboot_prop
; /* to be filled */
198 static ibft_status_t
iscsi_parse_ibft_control(iscsi_ibft_ctl_t
*ctl_hdr
,
199 ushort_t
*iscsi_offset_buf
);
201 static ibft_status_t
iscsi_parse_ibft_initiator(char *begin_of_ibft
,
202 iscsi_ibft_initiator_t
*initiator
);
204 static ibft_status_t
iscsi_parse_ibft_NIC(iscsi_ibft_nic_t
*nicp
);
206 static ibft_status_t
iscsi_parse_ibft_target(char *begin_of_ibft
,
207 iscsi_ibft_tgt_t
*tgtp
);
212 * Success: IBFT_STATUS_OK
213 * Fail: IBFT_STATUS_BADCHECKSUM
216 iscsi_ibft_hdr_checksum(iscsi_ibft_tbl_hdr_t
*tbl_hdr
)
218 uchar_t checksum
= 0;
219 uchar_t
*start
= NULL
;
223 if (tbl_hdr
== NULL
) {
224 return (IBFT_STATUS_BADHDR
);
227 length
= tbl_hdr
->Length
;
228 start
= (uchar_t
*)tbl_hdr
;
230 for (i
= 0; i
< length
; i
++) {
231 checksum
= checksum
+ start
[i
];
235 return (IBFT_STATUS_OK
);
237 return (IBFT_STATUS_BADCHECKSUM
);
241 * Now we only support one control structure in the IBFT.
242 * So there is no Control ID here.
245 iscsi_parse_ibft_structure(char *begin_of_ibft
, char *buf
)
247 iscsi_ibft_hdr_t
*hdr
= NULL
;
248 ibft_status_t ret
= IBFT_STATUS_OK
;
251 return (IBFT_STATUS_ERR
);
254 hdr
= (iscsi_ibft_hdr_t
*)buf
;
255 switch (hdr
->Structure_id
) {
257 ret
= iscsi_parse_ibft_initiator(
259 (iscsi_ibft_initiator_t
*)buf
);
262 ret
= iscsi_parse_ibft_NIC(
263 (iscsi_ibft_nic_t
*)buf
);
266 ret
= iscsi_parse_ibft_target(
268 (iscsi_ibft_tgt_t
*)buf
);
271 ret
= IBFT_STATUS_BADHDR
;
279 * Parse the iBFT table
280 * return IBFT_STATUS_OK upon sucess
283 iscsi_parse_ibft_tbl(iscsi_ibft_tbl_hdr_t
*tbl_hdr
)
287 ibft_status_t ret
= IBFT_STATUS_OK
;
288 ushort_t iscsi_offset_buf
[IBFT_OFFSET_BUF_LEN
] = {0};
290 if (tbl_hdr
== NULL
) {
291 return (IBFT_STATUS_ERR
);
294 if (iscsi_ibft_hdr_checksum(tbl_hdr
) != IBFT_STATUS_OK
) {
295 return (IBFT_STATUS_BADCHECKSUM
);
298 outbuf
= (char *)tbl_hdr
;
300 ret
= iscsi_parse_ibft_control(
301 (iscsi_ibft_ctl_t
*)&outbuf
[ISCSI_IBFT_CTL_OFFSET
],
304 if (ret
== IBFT_STATUS_OK
) {
305 ret
= IBFT_STATUS_ERR
;
306 for (i
= 0; i
< IBFT_OFFSET_BUF_LEN
; i
++) {
307 if (iscsi_offset_buf
[i
] != 0) {
308 ret
= iscsi_parse_ibft_structure(
311 iscsi_offset_buf
[i
]);
312 if (ret
!= IBFT_STATUS_OK
) {
323 iscsi_parse_ibft_control(iscsi_ibft_ctl_t
*ctl_hdr
,
324 ushort_t
*iscsi_offset_buf
)
327 ushort_t
*offsetp
= NULL
;
329 if (ctl_hdr
== NULL
) {
330 return (IBFT_STATUS_BADHDR
);
333 if (ctl_hdr
->header
.Structure_id
!= Control
) {
334 return (IBFT_STATUS_BADCID
);
338 * Copy the offsets to offset buffer.
340 for (offsetp
= &(ctl_hdr
->Initiator_offset
); i
< IBFT_OFFSET_BUF_LEN
;
342 iscsi_offset_buf
[i
++] = *offsetp
;
345 return (IBFT_STATUS_OK
);
349 * We only copy the "Firmare Boot Selseted" and valid initiator
350 * to the boot property.
353 iscsi_parse_ibft_initiator(char *begin_of_ibft
,
354 iscsi_ibft_initiator_t
*initiator
)
356 if (initiator
== NULL
) {
357 return (IBFT_STATUS_ERR
);
360 if (initiator
->header
.Structure_id
!= Initiator
) {
361 return (IBFT_STATUS_BADHDR
);
364 if ((initiator
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
365 (initiator
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
367 * If the initiator name exists, we will copy it to our own
370 if (initiator
->ini_name_len
!= 0) {
371 boot_property
.boot_init
.ini_name
=
372 (uchar_t
*)kmem_zalloc(
373 initiator
->ini_name_len
+ 1, KM_SLEEP
);
374 boot_property
.boot_init
.ini_name_len
=
375 initiator
->ini_name_len
+ 1;
377 (char *)boot_property
.boot_init
.ini_name
,
378 initiator
->ini_name_len
+ 1, "%s",
379 begin_of_ibft
+ initiator
->ini_name_offset
);
382 return (IBFT_STATUS_OK
);
386 iscsi_parse_ipaddr(uchar_t
*source
, char *dest
, int *af
)
390 if (source
== NULL
) {
391 return (IBFT_STATUS_ERR
);
394 if (source
[0] == 0x00 && source
[1] == 0x00 &&
395 source
[2] == 0x00 && source
[3] == 0x00 &&
396 source
[4] == 0x00 && source
[5] == 0x00 &&
397 source
[6] == 0x00 && source
[7] == 0x00 &&
398 source
[8] == 0x00 && source
[9] == 0x00 &&
399 (source
[10] == 0xff) && (source
[11] == 0xff)) {
404 (void) sprintf(dest
, "%d.%d.%d.%d",
405 source
[12], source
[13], source
[14], source
[15]);
412 for (i
= 0; i
< 14; i
= i
+ 2) {
413 (void) sprintf(dest
, "%02x%02x:", source
[i
],
417 (void) sprintf(dest
, "%02x%02x",
418 source
[i
], source
[i
+1]);
425 return (IBFT_STATUS_OK
);
429 * Copy the ip address from ibft. If IPv4 is used, we should copy
430 * the address from 12th byte.
433 iscsi_copy_ibft_ipaddr(uchar_t
*source
, void *dest
, int *af
)
435 ibft_status_t ret
= IBFT_STATUS_OK
;
438 if (source
== NULL
|| dest
== NULL
) {
439 return (IBFT_STATUS_ERR
);
441 ret
= iscsi_parse_ipaddr(source
, NULL
, &sin_family
);
443 return (IBFT_STATUS_BADIP
);
446 if (sin_family
== AF_INET
) {
447 bcopy(source
+IPV4_OFFSET
, dest
, sizeof (struct in_addr
));
448 } else if (sin_family
== AF_INET6
) {
449 bcopy(source
, dest
, sizeof (struct in6_addr
));
451 return (IBFT_STATUS_BADAF
);
457 return (IBFT_STATUS_OK
);
461 * Maybe there are multiply NICs are available. We only copy the
462 * "Firmare Boot Selseted" and valid one to the boot property.
465 iscsi_parse_ibft_NIC(iscsi_ibft_nic_t
*nicp
)
467 ibft_status_t ret
= IBFT_STATUS_OK
;
471 return (IBFT_STATUS_ERR
);
474 if (nicp
->header
.Structure_id
!= Nic
) {
475 return (IBFT_STATUS_ERR
);
478 if ((nicp
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
479 (nicp
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
480 ret
= iscsi_copy_ibft_ipaddr(nicp
->ip_addr
,
481 &boot_property
.boot_nic
.nic_ip_u
, &af
);
482 if (ret
!= IBFT_STATUS_OK
) {
486 boot_property
.boot_nic
.sin_family
= af
;
488 ret
= iscsi_copy_ibft_ipaddr(nicp
->Gateway
,
489 &boot_property
.boot_nic
.nic_gw_u
, NULL
);
490 if (ret
!= IBFT_STATUS_OK
) {
494 ret
= iscsi_copy_ibft_ipaddr(nicp
->dhcp
,
495 &boot_property
.boot_nic
.nic_dhcp_u
, NULL
);
496 if (ret
!= IBFT_STATUS_OK
) {
500 bcopy(nicp
->mac
, boot_property
.boot_nic
.nic_mac
, 6);
501 boot_property
.boot_nic
.sub_mask_prefix
=
502 nicp
->Subnet_Mask_Prefix
;
505 return (IBFT_STATUS_OK
);
509 * Maybe there are multiply targets are available. We only copy the
510 * "Firmare Boot Selseted" and valid one to the boot property.
513 iscsi_parse_ibft_target(char *begin_of_ibft
, iscsi_ibft_tgt_t
*tgtp
)
517 ibft_status_t ret
= IBFT_STATUS_OK
;
520 return (IBFT_STATUS_ERR
);
523 if (tgtp
->header
.Structure_id
!= Target
) {
524 return (IBFT_STATUS_BADHDR
);
527 if ((tgtp
->header
.Flags
& IBFT_FIRMWARE_BOOT_SELECTED
) &&
528 (tgtp
->header
.Flags
& IBFT_BLOCK_VALID_YES
)) {
532 ret
= iscsi_copy_ibft_ipaddr(tgtp
->ip_addr
,
533 &boot_property
.boot_tgt
.tgt_ip_u
, &af
);
534 if (ret
!= IBFT_STATUS_OK
) {
537 boot_property
.boot_tgt
.sin_family
= af
;
541 if (tgtp
->target_name_len
!= 0) {
542 boot_property
.boot_tgt
.tgt_name
=
543 (uchar_t
*)kmem_zalloc(tgtp
->target_name_len
+ 1,
545 boot_property
.boot_tgt
.tgt_name_len
=
546 tgtp
->target_name_len
+ 1;
548 (char *)boot_property
.boot_tgt
.tgt_name
,
549 tgtp
->target_name_len
+ 1, "%s",
550 begin_of_ibft
+ tgtp
->target_name_offset
);
552 boot_property
.boot_tgt
.tgt_name
= NULL
;
556 boot_property
.boot_tgt
.tgt_port
= tgtp
->port
;
558 boot_property
.boot_tgt
.lun_online
= 0;
561 * Get CHAP secret and name.
563 if (tgtp
->chap_type
!= NO_CHAP
) {
564 if (tgtp
->chap_name_len
!= 0) {
565 boot_property
.boot_init
.ini_chap_name
=
566 (uchar_t
*)kmem_zalloc(
567 tgtp
->chap_name_len
+ 1,
569 boot_property
.boot_init
.ini_chap_name_len
=
570 tgtp
->chap_name_len
+ 1;
572 boot_property
.boot_init
.ini_chap_name
;
575 tgtp
->chap_name_len
+ 1, "%s",
576 begin_of_ibft
+ tgtp
->chap_name_offset
);
579 * Just set NULL, initiator is able to deal
582 boot_property
.boot_init
.ini_chap_name
= NULL
;
585 if (tgtp
->chap_secret_len
!= 0) {
586 boot_property
.boot_init
.ini_chap_sec
=
587 (uchar_t
*)kmem_zalloc(
588 tgtp
->chap_secret_len
+ 1,
590 boot_property
.boot_init
.ini_chap_sec_len
=
591 tgtp
->chap_secret_len
+ 1;
592 bcopy(begin_of_ibft
+
593 tgtp
->chap_secret_offset
,
594 boot_property
.boot_init
.ini_chap_sec
,
595 tgtp
->chap_secret_len
);
597 boot_property
.boot_init
.ini_chap_sec
= NULL
;
598 return (IBFT_STATUS_ERR
);
601 if (tgtp
->chap_type
== Mutual_CHAP
) {
602 if (tgtp
->rev_chap_name_len
!= 0) {
603 boot_property
.boot_tgt
.tgt_chap_name
=
604 (uchar_t
*)kmem_zalloc(
605 tgtp
->rev_chap_name_len
+ 1,
607 boot_property
.boot_tgt
.tgt_chap_name_len
608 = tgtp
->rev_chap_name_len
+ 1;
609 #define TGT_CHAP_NAME boot_property.boot_tgt.tgt_chap_name
610 tmp
= (char *)TGT_CHAP_NAME
;
614 tgtp
->rev_chap_name_len
+ 1,
617 tgtp
->rev_chap_name_offset
);
620 * Just set NULL, initiator is able
623 boot_property
.boot_tgt
.tgt_chap_name
=
627 if (tgtp
->rev_chap_secret_len
!= 0) {
628 boot_property
.boot_tgt
.tgt_chap_sec
=
629 (uchar_t
*)kmem_zalloc(
630 tgtp
->rev_chap_secret_len
+ 1,
632 boot_property
.boot_tgt
.tgt_chap_sec_len
633 = tgtp
->rev_chap_secret_len
+ 1;
635 boot_property
.boot_tgt
.tgt_chap_sec
;
638 tgtp
->rev_chap_secret_len
+ 1,
641 tgtp
->chap_secret_offset
);
643 boot_property
.boot_tgt
.tgt_chap_sec
=
645 return (IBFT_STATUS_BADCHAPSEC
);
649 boot_property
.boot_init
.ini_chap_name
= NULL
;
650 boot_property
.boot_init
.ini_chap_sec
= NULL
;
656 (void) bcopy(tgtp
->boot_lun
,
657 boot_property
.boot_tgt
.tgt_boot_lun
, 8);
660 return (IBFT_STATUS_OK
);
664 * This function is used for scanning iBFT from the physical memory.
670 iscsi_scan_ibft_tbl(char *ibft_tbl_buf
)
675 ibft_status_t ret
= IBFT_STATUS_NOTABLE
;
677 for (start
= ISCSI_IBFT_LOWER_ADDR
; start
< ISCSI_IBFT_HIGHER_ADDR
;
678 start
= start
+ ISCSI_IBFT_ALIGNED
) {
679 va
= (void *)psm_map((paddr_t
)(start
&0xffffffff),
680 ISCSI_IBFT_SIGNATURE_LEN
,
686 if (memcmp(va
, ISCSI_IBFT_SIGNATRUE
,
687 ISCSI_IBFT_SIGNATURE_LEN
) == 0) {
688 ret
= IBFT_STATUS_ERR
;
689 /* Acquire table length */
690 len
= (int *)psm_map(
692 ISCSI_IBFT_SIGNATURE_LEN
)&0xffffffff),
693 ISCSI_IBFT_SIGNATURE_LEN
, PROT_READ
);
695 psm_unmap((caddr_t
)va
,
696 ISCSI_IBFT_SIGNATURE_LEN
);
699 if (ISCSI_IBFT_LOWER_ADDR
+ *len
<
700 ISCSI_IBFT_HIGHER_ADDR
- 1) {
702 ISCSI_IBFT_SIGNATURE_LEN
);
703 va
= psm_map((paddr_t
)(start
&0xffffffff),
708 * Copy data to our own buffer
710 bcopy(va
, ibft_tbl_buf
, *len
);
711 ret
= IBFT_STATUS_OK
;
713 psm_unmap((caddr_t
)va
, *len
);
714 psm_unmap((caddr_t
)len
,
715 ISCSI_IBFT_SIGNATURE_LEN
);
718 psm_unmap((caddr_t
)va
,
719 ISCSI_IBFT_SIGNATURE_LEN
);
720 psm_unmap((caddr_t
)len
,
721 ISCSI_IBFT_SIGNATURE_LEN
);
724 psm_unmap((caddr_t
)va
, ISCSI_IBFT_SIGNATURE_LEN
);
732 * Scan the ibft table and store the iSCSI boot properties
733 * If there is a valid table then set the iscsiboot_prop
734 * iBF should be off if the host is not intended
735 * to be booted from iSCSI disk
740 ibft_status_t ret
= IBFT_STATUS_OK
;
743 if (do_bsys_getproplen(NULL
, "ibft-noprobe") > 0)
746 if (ibft_noprobe
!= 0) {
748 * Scanning for iBFT may conflict with devices which use memory
749 * in 640-1024KB of physical address space. The iBFT
750 * specification suggests use of low RAM method - scanning
751 * physical memory 512-1024 KB for iBFT table. However, the
752 * Upper Memory Area (UMA) 640-1024 KB may contain device
753 * memory or memory mapped I/O. Although reading from I/O area
754 * is usually fine, the actual behavior depends on device
755 * implementation. In some cases, the user may want to disable
756 * low RAM method and prevent reading from device I/O area.
758 * To disable low RAM method:
759 * 1) pass "-B ibft-noprobe=1" on kernel command line
760 * 2) add line "set ibft_noprobe=1" in /etc/system
762 cmn_err(CE_NOTE
, IBFT_NOPROBE_MSG
);
766 ibft_tbl_buf
= (char *)kmem_zalloc(ISCSI_IBFT_TBL_BUF_LEN
,
770 /* Unlikely to happen */
771 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
,
776 (void) memset(&boot_property
, 0, sizeof (boot_property
));
777 if ((ret
= iscsi_scan_ibft_tbl(ibft_tbl_buf
)) ==
779 ret
= iscsi_parse_ibft_tbl(
780 (iscsi_ibft_tbl_hdr_t
*)ibft_tbl_buf
);
781 if (ret
== IBFT_STATUS_OK
) {
782 iscsiboot_prop
= &boot_property
;
783 iscsi_print_boot_property();
785 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
, ret
);
787 } else if (ret
!= IBFT_STATUS_NOTABLE
) {
788 cmn_err(CE_NOTE
, IBFT_INVALID_MSG
, ret
);
791 kmem_free(ibft_tbl_buf
, ISCSI_IBFT_TBL_BUF_LEN
);