xref: /titanic_44/usr/src/uts/i86pc/os/biosdisk.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/controlregs.h>
32 #include <sys/bootconf.h>
33 #include <sys/bootvfs.h>
34 #include <sys/bootregs.h>
35 #include <sys/bootconf.h>
36 #include <sys/conf.h>
37 #include <sys/promif.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 #include <sys/biosdisk.h>
42 #include <sys/psw.h>
43 
44 
45 /* hard code realmode memory address for now */
46 #define	BIOS_RES_BUFFER_ADDR		0x7000
47 
48 #define	BIOSDEV_NUM	8
49 #define	STARTING_DRVNUM	0x80
50 #define	FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF)
51 #define	FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
52 
53 #ifdef DEBUG
54 int biosdebug = 0;
55 #define	dprintf(fmt) \
56 	if (biosdebug) \
57 		prom_printf fmt
58 #else
59 #define	dprintf(fmt)
60 #endif
61 
62 biosdev_data_t biosdev_info[BIOSDEV_NUM]; /* from 0x80 to 0x87 */
63 int dobiosdev = 1;
64 
65 
66 static int bios_check_extension_present(uchar_t);
67 static int get_dev_params(uchar_t);
68 static int read_firstblock(uchar_t drivenum);
69 static int drive_present(uchar_t drivenum);
70 static void reset_disk(uchar_t drivenum);
71 
72 
73 void
74 startup_bios_disk()
75 {
76 	uchar_t drivenum;
77 	int got_devparams = 0;
78 	int got_first_block = 0;
79 	uchar_t	name[20];
80 	dev_info_t	*devi;
81 
82 	if (dobiosdev == 0)
83 		return;
84 
85 	for (drivenum = 0x80; drivenum < (0x80 + BIOSDEV_NUM); drivenum++) {
86 
87 		if (!drive_present(drivenum))
88 			continue;
89 
90 		got_devparams = get_dev_params(drivenum);
91 
92 		if ((got_first_block = read_firstblock(drivenum)) == 0) {
93 			/* retry */
94 			got_first_block = read_firstblock(drivenum);
95 		}
96 
97 		if (got_devparams || got_first_block) {
98 			(void) sprintf((char *)name, "biosdev-0x%x", drivenum);
99 			devi = ddi_root_node();
100 			(void) e_ddi_prop_update_byte_array(DDI_DEV_T_NONE,
101 			    devi, (char *)name,
102 			    (uchar_t *)&biosdev_info[drivenum - 0x80],
103 			    sizeof (biosdev_data_t));
104 		}
105 	}
106 }
107 
108 static int
109 bios_check_extension_present(uchar_t drivenum)
110 {
111 	struct bop_regs rp = {0};
112 	extern struct bootops		*bootops;
113 
114 	rp.eax.word.ax = 0x4100;
115 	rp.ebx.word.bx = 0x55AA;
116 	rp.edx.word.dx = drivenum;
117 
118 	/* make sure we have extension support */
119 	BOP_DOINT(bootops, 0x13, &rp);
120 
121 	if (((rp.eflags & PS_C) != 0) || (rp.ebx.word.bx != 0xAA55)) {
122 		dprintf(("bios_check_extension_present int13 fn 41 "
123 		    "failed %d bx = %x\n", rp.eflags, rp.ebx.word.bx));
124 		return (0);
125 	}
126 
127 	if ((rp.ecx.word.cx & 0x7) == 0) {
128 		dprintf(("bios_check_extension_present get device parameters "
129 		    "not supported cx = %x\n", rp.ecx.word.cx));
130 		return (0);
131 	}
132 
133 	return (1);
134 }
135 
136 static int
137 get_dev_params(uchar_t drivenum)
138 {
139 	struct bop_regs rp = {0};
140 	fn48_t	 *bufp;
141 	extern struct bootops		*bootops;
142 	int i;
143 	int index;
144 
145 	dprintf(("In get_dev_params\n"));
146 
147 	if (bios_check_extension_present(drivenum) == 0)
148 		return (0);
149 
150 	bufp = (fn48_t *)BIOS_RES_BUFFER_ADDR;
151 
152 	for (i = 0; i < sizeof (*bufp); i++)
153 		((uchar_t *)bufp)[i] = 0;
154 
155 	bufp->buflen = sizeof (*bufp);
156 	rp.eax.word.ax = 0x4800;
157 	rp.edx.byte.dl = drivenum;
158 
159 	rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
160 	rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
161 
162 	BOP_DOINT(bootops, 0x13, &rp);
163 
164 	if ((rp.eflags & PS_C) != 0) {
165 		dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
166 		    rp.eflags, rp.eax.byte.ah));
167 		return (0);
168 	}
169 
170 	index = drivenum - 0x80;
171 	biosdev_info[index].edd_valid = 1;
172 	biosdev_info[index].fn48_dev_params = *bufp;
173 
174 	return (1);
175 
176 }
177 
178 static int
179 drive_present(uchar_t drivenum)
180 {
181 	struct bop_regs rp = {0};
182 
183 	rp.eax.byte.ah = 0x8;	/* get params */
184 	rp.edx.byte.dl = drivenum;
185 
186 	BOP_DOINT(bootops, 0x13, &rp);
187 
188 	if (((rp.eflags & PS_C) != 0) || rp.eax.byte.ah != 0) {
189 		dprintf(("drive not present drivenum %x eflag %x ah %x\n",
190 		drivenum, rp.eflags, rp.eax.byte.ah));
191 		return (0);
192 	}
193 
194 	dprintf(("drive-present %x\n", drivenum));
195 	return (1);
196 
197 }
198 
199 static void
200 reset_disk(uchar_t drivenum)
201 {
202 	struct bop_regs rp = {0};
203 	int status;
204 
205 	rp.eax.byte.ah = 0x0;   /* reset disk */
206 	rp.edx.byte.dl = drivenum;
207 
208 	BOP_DOINT(bootops, 0x13, &rp);
209 
210 	status = rp.eax.byte.ah;
211 
212 	if (((rp.eflags & PS_C) != 0) || status != 0)
213 		dprintf(("Bad disk reset driv %x, status %x\n", drivenum,
214 		    status));
215 }
216 
217 /* Get first block */
218 static int
219 read_firstblock(uchar_t drivenum)
220 {
221 
222 	struct bop_regs rp = {0};
223 	caddr_t	 bufp;
224 	uchar_t status;
225 	int i, index;
226 
227 
228 	reset_disk(drivenum);
229 	bufp = (caddr_t)BIOS_RES_BUFFER_ADDR;
230 
231 
232 	rp.eax.byte.ah = 0x2; 	/* Read disk */
233 	rp.eax.byte.al = 1;	/* nsect */
234 	rp.ecx.byte.ch = 0;	/* cyl & 0xff */
235 	rp.ecx.byte.cl = 1;	/* cyl >> 2 & 0xc0 (sector number) */
236 	rp.edx.byte.dh = 0;	/* head */
237 	rp.edx.byte.dl = drivenum;	/* drivenum */
238 
239 	/* es:bx is buf address */
240 	rp.ebx.word.bx = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
241 	rp.es = FP_SEG((uint_t)(uintptr_t)bufp);
242 
243 	BOP_DOINT(bootops, 0x13, &rp);
244 
245 	status = rp.eax.byte.ah;
246 	if (((rp.eflags & PS_C) != 0) || status != 0) {
247 		dprintf(("read_firstblock AH not clear %x \n", status));
248 		return (0);
249 	}
250 
251 	dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum,
252 	    *(uint32_t *)(bufp +0x1b8)));
253 
254 	index = drivenum - 0x80;
255 
256 	biosdev_info[index].first_block_valid = 1;
257 	for (i = 0; i < 512; i++)
258 		biosdev_info[index].first_block[i] = *((uchar_t *)bufp + i);
259 
260 	return (1);
261 }
262