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 #include <sys/hypervisor.h>
44 extern int prom_debug
;
46 /* hard code realmode memory address for now */
47 #define BIOS_RES_BUFFER_ADDR 0x7000
50 #define STARTING_DRVNUM 0x80
51 #define FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF)
52 #define FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
56 #define dprintf(fmt) \
63 biosdev_data_t biosdev_info
[BIOSDEV_NUM
]; /* from 0x80 to 0x87 */
67 static int bios_check_extension_present(uchar_t
);
68 static int get_dev_params(uchar_t
);
69 static int read_firstblock(uchar_t drivenum
);
70 static int drive_present(uchar_t drivenum
);
71 static void reset_disk(uchar_t drivenum
);
72 static int is_eltorito(uchar_t drivenum
);
79 int got_devparams
= 0;
80 int got_first_block
= 0;
88 for (drivenum
= 0x80; drivenum
< (0x80 + BIOSDEV_NUM
); drivenum
++) {
90 if (!drive_present(drivenum
))
93 extensions
= bios_check_extension_present(drivenum
);
96 * If we're booting from an Eltorito CD/DVD image, there's
97 * no need to get the device parameters or read the first block
98 * because we'll never install onto this device.
100 if (extensions
&& is_eltorito(drivenum
))
103 if (extensions
&& get_dev_params(drivenum
))
108 if ((got_first_block
= read_firstblock(drivenum
)) == 0) {
110 got_first_block
= read_firstblock(drivenum
);
113 if (got_devparams
|| got_first_block
) {
114 (void) sprintf((char *)name
, "biosdev-0x%x", drivenum
);
115 devi
= ddi_root_node();
116 (void) e_ddi_prop_update_byte_array(DDI_DEV_T_NONE
,
118 (uchar_t
*)&biosdev_info
[drivenum
- 0x80],
119 sizeof (biosdev_data_t
));
126 bios_check_extension_present(uchar_t drivenum
)
128 struct bop_regs rp
= {0};
129 extern struct bootops
*bootops
;
131 rp
.eax
.word
.ax
= 0x4100;
132 rp
.ebx
.word
.bx
= 0x55AA;
133 rp
.edx
.word
.dx
= drivenum
;
135 /* make sure we have extension support */
136 BOP_DOINT(bootops
, 0x13, &rp
);
138 if (((rp
.eflags
& PS_C
) != 0) || (rp
.ebx
.word
.bx
!= 0xAA55)) {
139 dprintf(("bios_check_extension_present int13 fn 41 "
140 "failed %d bx = %x\n", rp
.eflags
, rp
.ebx
.word
.bx
));
144 if ((rp
.ecx
.word
.cx
& 0x7) == 0) {
145 dprintf(("bios_check_extension_present get device parameters "
146 "not supported cx = %x\n", rp
.ecx
.word
.cx
));
154 get_dev_params(uchar_t drivenum
)
156 struct bop_regs rp
= {0};
158 extern struct bootops
*bootops
;
163 dprintf(("In get_dev_params\n"));
165 bufp
= (fn48_t
*)BIOS_RES_BUFFER_ADDR
;
168 * We cannot use bzero here as we're initializing data
169 * at an address below kernel base.
171 for (i
= 0; i
< sizeof (*bufp
); i
++)
172 ((uchar_t
*)bufp
)[i
] = 0;
174 bufp
->buflen
= sizeof (*bufp
);
175 rp
.eax
.word
.ax
= 0x4800;
176 rp
.edx
.byte
.dl
= drivenum
;
178 rp
.esi
.word
.si
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
179 rp
.ds
= FP_SEG((uint_t
)(uintptr_t)bufp
);
181 BOP_DOINT(bootops
, 0x13, &rp
);
183 if ((rp
.eflags
& PS_C
) != 0) {
184 dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
185 rp
.eflags
, rp
.eax
.byte
.ah
));
189 index
= drivenum
- 0x80;
190 biosdev_info
[index
].edd_valid
= 1;
193 * Some compilers turn a structure copy into a call
194 * to memcpy. Since we are copying data below kernel
195 * base intentionally, and memcpy asserts that's not
196 * the case, we do the copy manually here.
198 tmp
= (uchar_t
*)&biosdev_info
[index
].fn48_dev_params
;
199 for (i
= 0; i
< sizeof (*bufp
); i
++)
200 tmp
[i
] = ((uchar_t
*)bufp
)[i
];
206 drive_present(uchar_t drivenum
)
208 struct bop_regs rp
= {0};
210 rp
.eax
.byte
.ah
= 0x8; /* get params */
211 rp
.edx
.byte
.dl
= drivenum
;
213 BOP_DOINT(bootops
, 0x13, &rp
);
215 if (((rp
.eflags
& PS_C
) != 0) || rp
.eax
.byte
.ah
!= 0) {
216 dprintf(("drive not present drivenum %x eflag %x ah %x\n",
217 drivenum
, rp
.eflags
, rp
.eax
.byte
.ah
));
221 dprintf(("drive-present %x\n", drivenum
));
226 reset_disk(uchar_t drivenum
)
228 struct bop_regs rp
= {0};
231 rp
.eax
.byte
.ah
= 0x0; /* reset disk */
232 rp
.edx
.byte
.dl
= drivenum
;
234 BOP_DOINT(bootops
, 0x13, &rp
);
236 status
= rp
.eax
.byte
.ah
;
238 if (((rp
.eflags
& PS_C
) != 0) || status
!= 0)
239 dprintf(("Bad disk reset driv %x, status %x\n", drivenum
,
243 /* Get first block */
245 read_firstblock(uchar_t drivenum
)
248 struct bop_regs rp
= {0};
254 reset_disk(drivenum
);
255 bufp
= (caddr_t
)BIOS_RES_BUFFER_ADDR
;
258 rp
.eax
.byte
.ah
= 0x2; /* Read disk */
259 rp
.eax
.byte
.al
= 1; /* nsect */
260 rp
.ecx
.byte
.ch
= 0; /* cyl & 0xff */
261 rp
.ecx
.byte
.cl
= 1; /* cyl >> 2 & 0xc0 (sector number) */
262 rp
.edx
.byte
.dh
= 0; /* head */
263 rp
.edx
.byte
.dl
= drivenum
; /* drivenum */
265 /* es:bx is buf address */
266 rp
.ebx
.word
.bx
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
267 rp
.es
= FP_SEG((uint_t
)(uintptr_t)bufp
);
269 BOP_DOINT(bootops
, 0x13, &rp
);
271 status
= rp
.eax
.byte
.ah
;
272 if (((rp
.eflags
& PS_C
) != 0) || status
!= 0) {
273 dprintf(("read_firstblock AH not clear %x \n", status
));
277 dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum
,
278 *(uint32_t *)(bufp
+0x1b8)));
280 index
= drivenum
- 0x80;
282 biosdev_info
[index
].first_block_valid
= 1;
283 for (i
= 0; i
< 512; i
++)
284 biosdev_info
[index
].first_block
[i
] = *((uchar_t
*)bufp
+ i
);
290 is_eltorito(uchar_t drivenum
)
292 struct bop_regs rp
= {0};
294 extern struct bootops
*bootops
;
297 dprintf(("In is_eltorito\n"));
299 bufp
= (fn4b_t
*)BIOS_RES_BUFFER_ADDR
;
302 * We cannot use bzero here as we're initializing data
303 * at an address below kernel base.
305 for (i
= 0; i
< sizeof (*bufp
); i
++)
306 ((uchar_t
*)bufp
)[i
] = 0;
308 bufp
->pkt_size
= sizeof (*bufp
);
309 rp
.eax
.word
.ax
= 0x4b01;
310 rp
.edx
.byte
.dl
= drivenum
;
312 rp
.esi
.word
.si
= (uint16_t)FP_OFF((uint_t
)(uintptr_t)bufp
);
313 rp
.ds
= FP_SEG((uint_t
)(uintptr_t)bufp
);
315 BOP_DOINT(bootops
, 0x13, &rp
);
317 if ((rp
.eflags
& PS_C
) != 0 || bufp
->drivenum
!= drivenum
) {
318 dprintf(("fn 0x4b01 FAILED on drive "
319 "eflags=%x ah=%x drivenum=%x\n",
320 rp
.eflags
, rp
.eax
.byte
.ah
, bufp
->drivenum
));
325 prom_printf("INT13 FN4B01 mtype => %x", bufp
->boot_mtype
);