xref: /illumos-gate/usr/src/uts/i86pc/os/biosdisk.c (revision 9a016c63ca347047a236dff12f0da83aac8981d1)
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 	uchar_t *tmp;
145 
146 	dprintf(("In get_dev_params\n"));
147 
148 	if (bios_check_extension_present(drivenum) == 0)
149 		return (0);
150 
151 	bufp = (fn48_t *)BIOS_RES_BUFFER_ADDR;
152 
153 	/*
154 	 * We cannot use bzero here as we're initializing data
155 	 * at an address below kernel base.
156 	 */
157 	for (i = 0; i < sizeof (*bufp); i++)
158 		((uchar_t *)bufp)[i] = 0;
159 
160 	bufp->buflen = sizeof (*bufp);
161 	rp.eax.word.ax = 0x4800;
162 	rp.edx.byte.dl = drivenum;
163 
164 	rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
165 	rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
166 
167 	BOP_DOINT(bootops, 0x13, &rp);
168 
169 	if ((rp.eflags & PS_C) != 0) {
170 		dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
171 		    rp.eflags, rp.eax.byte.ah));
172 		return (0);
173 	}
174 
175 	index = drivenum - 0x80;
176 	biosdev_info[index].edd_valid = 1;
177 
178 	/*
179 	 * Some compilers turn a structure copy into a call
180 	 * to memcpy.  Since we are copying data below kernel
181 	 * base intentionally, and memcpy asserts that's not
182 	 * the case, we do the copy manually here.
183 	 */
184 	tmp = (uchar_t *)&biosdev_info[index].fn48_dev_params;
185 	for (i = 0; i < sizeof (*bufp); i++)
186 		tmp[i] = ((uchar_t *)bufp)[i];
187 
188 	return (1);
189 }
190 
191 static int
192 drive_present(uchar_t drivenum)
193 {
194 	struct bop_regs rp = {0};
195 
196 	rp.eax.byte.ah = 0x8;	/* get params */
197 	rp.edx.byte.dl = drivenum;
198 
199 	BOP_DOINT(bootops, 0x13, &rp);
200 
201 	if (((rp.eflags & PS_C) != 0) || rp.eax.byte.ah != 0) {
202 		dprintf(("drive not present drivenum %x eflag %x ah %x\n",
203 		drivenum, rp.eflags, rp.eax.byte.ah));
204 		return (0);
205 	}
206 
207 	dprintf(("drive-present %x\n", drivenum));
208 	return (1);
209 
210 }
211 
212 static void
213 reset_disk(uchar_t drivenum)
214 {
215 	struct bop_regs rp = {0};
216 	int status;
217 
218 	rp.eax.byte.ah = 0x0;   /* reset disk */
219 	rp.edx.byte.dl = drivenum;
220 
221 	BOP_DOINT(bootops, 0x13, &rp);
222 
223 	status = rp.eax.byte.ah;
224 
225 	if (((rp.eflags & PS_C) != 0) || status != 0)
226 		dprintf(("Bad disk reset driv %x, status %x\n", drivenum,
227 		    status));
228 }
229 
230 /* Get first block */
231 static int
232 read_firstblock(uchar_t drivenum)
233 {
234 
235 	struct bop_regs rp = {0};
236 	caddr_t	 bufp;
237 	uchar_t status;
238 	int i, index;
239 
240 
241 	reset_disk(drivenum);
242 	bufp = (caddr_t)BIOS_RES_BUFFER_ADDR;
243 
244 
245 	rp.eax.byte.ah = 0x2; 	/* Read disk */
246 	rp.eax.byte.al = 1;	/* nsect */
247 	rp.ecx.byte.ch = 0;	/* cyl & 0xff */
248 	rp.ecx.byte.cl = 1;	/* cyl >> 2 & 0xc0 (sector number) */
249 	rp.edx.byte.dh = 0;	/* head */
250 	rp.edx.byte.dl = drivenum;	/* drivenum */
251 
252 	/* es:bx is buf address */
253 	rp.ebx.word.bx = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
254 	rp.es = FP_SEG((uint_t)(uintptr_t)bufp);
255 
256 	BOP_DOINT(bootops, 0x13, &rp);
257 
258 	status = rp.eax.byte.ah;
259 	if (((rp.eflags & PS_C) != 0) || status != 0) {
260 		dprintf(("read_firstblock AH not clear %x \n", status));
261 		return (0);
262 	}
263 
264 	dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum,
265 	    *(uint32_t *)(bufp +0x1b8)));
266 
267 	index = drivenum - 0x80;
268 
269 	biosdev_info[index].first_block_valid = 1;
270 	for (i = 0; i < 512; i++)
271 		biosdev_info[index].first_block[i] = *((uchar_t *)bufp + i);
272 
273 	return (1);
274 }
275