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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/controlregs.h>
29 #include <sys/bootconf.h>
30 #include <sys/bootvfs.h>
31 #include <sys/bootregs.h>
32 #include <sys/bootconf.h>
34 #include <sys/promif.h>
36 #include <sys/sunddi.h>
37 #include <sys/sunndi.h>
38 #include <sys/biosdisk.h>
41 extern int prom_debug
;
43 /* hard code realmode memory address for now */
44 #define BIOS_RES_BUFFER_ADDR 0x7000
47 #define STARTING_DRVNUM 0x80
48 #define FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF)
49 #define FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
53 #define dprintf(fmt) \
60 biosdev_data_t biosdev_info
[BIOSDEV_NUM
]; /* from 0x80 to 0x87 */
63 static int bios_check_extension_present(uchar_t
);
64 static int get_dev_params(uchar_t
);
65 static int read_firstblock(uchar_t drivenum
);
66 static int drive_present(uchar_t drivenum
);
67 static void reset_disk(uchar_t drivenum
);
68 static int is_eltorito(uchar_t drivenum
);
74 int got_devparams
= 0;
75 int got_first_block
= 0;
80 for (drivenum
= 0x80; drivenum
< (0x80 + BIOSDEV_NUM
); drivenum
++) {
82 if (!drive_present(drivenum
))
85 extensions
= bios_check_extension_present(drivenum
);
88 * If we're booting from an Eltorito CD/DVD image, there's
89 * no need to get the device parameters or read the first block
90 * because we'll never install onto this device.
92 if (extensions
&& is_eltorito(drivenum
))
95 if (extensions
&& get_dev_params(drivenum
))
100 if ((got_first_block
= read_firstblock(drivenum
)) == 0) {
102 got_first_block
= read_firstblock(drivenum
);
105 if (got_devparams
|| got_first_block
) {
106 (void) sprintf((char *)name
, "biosdev-0x%x", drivenum
);
107 devi
= ddi_root_node();
108 (void) e_ddi_prop_update_byte_array(DDI_DEV_T_NONE
,
110 (uchar_t
*)&biosdev_info
[drivenum
- 0x80],
111 sizeof (biosdev_data_t
));
117 bios_check_extension_present(uchar_t drivenum
)
119 struct bop_regs rp
= {0};
120 extern struct bootops
*bootops
;
122 rp
.eax
.word
.ax
= 0x4100;
123 rp
.ebx
.word
.bx
= 0x55AA;
124 rp
.edx
.word
.dx
= drivenum
;
126 /* make sure we have extension support */
127 BOP_DOINT(bootops
, 0x13, &rp
);
129 if (((rp
.eflags
& PS_C
) != 0) || (rp
.ebx
.word
.bx
!= 0xAA55)) {
130 dprintf(("bios_check_extension_present int13 fn 41 "
131 "failed %d bx = %x\n", rp
.eflags
, rp
.ebx
.word
.bx
));
135 if ((rp
.ecx
.word
.cx
& 0x7) == 0) {
136 dprintf(("bios_check_extension_present get device parameters "
137 "not supported cx = %x\n", rp
.ecx
.word
.cx
));
145 get_dev_params(uchar_t drivenum
)
147 struct bop_regs rp
= {0};
149 extern struct bootops
*bootops
;
154 dprintf(("In get_dev_params\n"));
156 bufp
= (fn48_t
*)BIOS_RES_BUFFER_ADDR
;
159 * We cannot use bzero here as we're initializing data
160 * at an address below kernel base.
162 for (i
= 0; i
< sizeof (*bufp
); i
++)
163 ((uchar_t
*)bufp
)[i
] = 0;
165 bufp
->buflen
= sizeof (*bufp
);
166 rp
.eax
.word
.ax
= 0x4800;
167 rp
.edx
.byte
.dl
= drivenum
;
169 rp
.esi
.word
.si
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
170 rp
.ds
= FP_SEG((uint_t
)(uintptr_t)bufp
);
172 BOP_DOINT(bootops
, 0x13, &rp
);
174 if ((rp
.eflags
& PS_C
) != 0) {
175 dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
176 rp
.eflags
, rp
.eax
.byte
.ah
));
180 index
= drivenum
- 0x80;
181 biosdev_info
[index
].edd_valid
= 1;
184 * Some compilers turn a structure copy into a call
185 * to memcpy. Since we are copying data below kernel
186 * base intentionally, and memcpy asserts that's not
187 * the case, we do the copy manually here.
189 tmp
= (uchar_t
*)&biosdev_info
[index
].fn48_dev_params
;
190 for (i
= 0; i
< sizeof (*bufp
); i
++)
191 tmp
[i
] = ((uchar_t
*)bufp
)[i
];
197 drive_present(uchar_t drivenum
)
199 struct bop_regs rp
= {0};
201 rp
.eax
.byte
.ah
= 0x8; /* get params */
202 rp
.edx
.byte
.dl
= drivenum
;
204 BOP_DOINT(bootops
, 0x13, &rp
);
206 if (((rp
.eflags
& PS_C
) != 0) || rp
.eax
.byte
.ah
!= 0) {
207 dprintf(("drive not present drivenum %x eflag %x ah %x\n",
208 drivenum
, rp
.eflags
, rp
.eax
.byte
.ah
));
212 dprintf(("drive-present %x\n", drivenum
));
217 reset_disk(uchar_t drivenum
)
219 struct bop_regs rp
= {0};
222 rp
.eax
.byte
.ah
= 0x0; /* reset disk */
223 rp
.edx
.byte
.dl
= drivenum
;
225 BOP_DOINT(bootops
, 0x13, &rp
);
227 status
= rp
.eax
.byte
.ah
;
229 if (((rp
.eflags
& PS_C
) != 0) || status
!= 0)
230 dprintf(("Bad disk reset driv %x, status %x\n", drivenum
,
234 /* Get first block */
236 read_firstblock(uchar_t drivenum
)
239 struct bop_regs rp
= {0};
245 reset_disk(drivenum
);
246 bufp
= (caddr_t
)BIOS_RES_BUFFER_ADDR
;
249 rp
.eax
.byte
.ah
= 0x2; /* Read disk */
250 rp
.eax
.byte
.al
= 1; /* nsect */
251 rp
.ecx
.byte
.ch
= 0; /* cyl & 0xff */
252 rp
.ecx
.byte
.cl
= 1; /* cyl >> 2 & 0xc0 (sector number) */
253 rp
.edx
.byte
.dh
= 0; /* head */
254 rp
.edx
.byte
.dl
= drivenum
; /* drivenum */
256 /* es:bx is buf address */
257 rp
.ebx
.word
.bx
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
258 rp
.es
= FP_SEG((uint_t
)(uintptr_t)bufp
);
260 BOP_DOINT(bootops
, 0x13, &rp
);
262 status
= rp
.eax
.byte
.ah
;
263 if (((rp
.eflags
& PS_C
) != 0) || status
!= 0) {
264 dprintf(("read_firstblock AH not clear %x \n", status
));
268 dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum
,
269 *(uint32_t *)(bufp
+0x1b8)));
271 index
= drivenum
- 0x80;
273 biosdev_info
[index
].first_block_valid
= 1;
274 for (i
= 0; i
< 512; i
++)
275 biosdev_info
[index
].first_block
[i
] = *((uchar_t
*)bufp
+ i
);
281 is_eltorito(uchar_t drivenum
)
283 struct bop_regs rp
= {0};
285 extern struct bootops
*bootops
;
288 dprintf(("In is_eltorito\n"));
290 bufp
= (fn4b_t
*)BIOS_RES_BUFFER_ADDR
;
293 * We cannot use bzero here as we're initializing data
294 * at an address below kernel base.
296 for (i
= 0; i
< sizeof (*bufp
); i
++)
297 ((uchar_t
*)bufp
)[i
] = 0;
299 bufp
->pkt_size
= sizeof (*bufp
);
300 rp
.eax
.word
.ax
= 0x4b01;
301 rp
.edx
.byte
.dl
= drivenum
;
303 rp
.esi
.word
.si
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
304 rp
.ds
= FP_SEG((uint_t
)(uintptr_t)bufp
);
306 BOP_DOINT(bootops
, 0x13, &rp
);
308 if ((rp
.eflags
& PS_C
) != 0 || bufp
->drivenum
!= drivenum
) {
309 dprintf(("fn 0x4b01 FAILED on drive "
310 "eflags=%x ah=%x drivenum=%x\n",
311 rp
.eflags
, rp
.eax
.byte
.ah
, bufp
->drivenum
));
316 prom_printf("INT13 FN4B01 mtype => %x", bufp
->boot_mtype
);