xref: /netbsd-src/sys/arch/i386/stand/lib/biosdisk_ll.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: biosdisk_ll.c,v 1.25 2006/11/18 19:58:56 erh Exp $	 */
2 
3 /*-
4  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Bang Jun-Young.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1996
41  * 	Matthias Drochner.  All rights reserved.
42  * Copyright (c) 1996
43  * 	Perry E. Metzger.  All rights reserved.
44  *
45  * Redistribution and use in source and binary forms, with or without
46  * modification, are permitted provided that the following conditions
47  * are met:
48  * 1. Redistributions of source code must retain the above copyright
49  *    notice, this list of conditions and the following disclaimer.
50  * 2. Redistributions in binary form must reproduce the above copyright
51  *    notice, this list of conditions and the following disclaimer in the
52  *    documentation and/or other materials provided with the distribution.
53  * 3. All advertising materials mentioning features or use of this software
54  *    must display the following acknowledgements:
55  *	This product includes software developed for the NetBSD Project
56  *	by Matthias Drochner.
57  *	This product includes software developed for the NetBSD Project
58  *	by Perry E. Metzger.
59  * 4. The names of the authors may not be used to endorse or promote products
60  *    derived from this software without specific prior written permission.
61  *
62  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
63  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
64  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
65  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
66  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
67  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
68  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
69  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
70  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
71  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72  */
73 
74 /*
75  * shared by bootsector startup (bootsectmain) and biosdisk.c
76  * needs lowlevel parts from bios_disk.S
77  */
78 
79 #include <lib/libsa/stand.h>
80 
81 #include "biosdisk_ll.h"
82 #include "diskbuf.h"
83 #include "libi386.h"
84 
85 static int do_read(struct biosdisk_ll *, daddr_t, int, char *);
86 
87 /*
88  * we get from get_diskinfo():
89  *      %ah      %ch      %cl      %dh (registers after int13/8), ie
90  * xxxxxxxx cccccccc CCssssss hhhhhhhh
91  */
92 #define STATUS(di)	((di)>>24)
93 #define SPT(di)		(((di)>>8)&0x3f)
94 #define HEADS(di)	(((di)&0xff)+1)
95 #define CYL(di)		(((((di)>>16)&0xff)|(((di)>>6)&0x300))+1)
96 
97 #ifndef BIOSDISK_RETRIES
98 #define BIOSDISK_RETRIES 5
99 #endif
100 
101 int
102 set_geometry(struct biosdisk_ll *d, struct biosdisk_extinfo *ed)
103 {
104 	int diskinfo;
105 
106 	diskinfo = biosdisk_getinfo(d->dev);
107 	d->sec = SPT(diskinfo);
108 	d->head = HEADS(diskinfo);
109 	d->cyl = CYL(diskinfo);
110 	d->chs_sectors = d->sec * d->head * d->cyl;
111 
112 	if (d->dev >= 0x80 + get_harddrives()) {
113 		d->secsize = 2048;
114 		d->type = BIOSDISK_TYPE_CD;
115 	} else {
116 		d->secsize = 512;
117 		if (d->dev & 0x80)
118 			d->type = BIOSDISK_TYPE_HD;
119 		else
120 			d->type = BIOSDISK_TYPE_FD;
121 	}
122 
123 	/*
124 	 * Some broken BIOSes such as one found on Soltek SL-75DRV2 report
125 	 * that they don't support int13 extension for CD-ROM drives while
126 	 * they actually do. As a workaround, if the boot device is a CD we
127 	 * assume that the extension is available. Note that only very old
128 	 * BIOSes don't support the extended mode, and they don't work with
129 	 * ATAPI CD-ROM drives, either. So there's no problem.
130 	 */
131 	d->flags = 0;
132 	if (d->type == BIOSDISK_TYPE_CD ||
133 	    (d->type == BIOSDISK_TYPE_HD && biosdisk_int13ext(d->dev))) {
134 		d->flags |= BIOSDISK_INT13EXT;
135 		if (ed != NULL) {
136 			ed->size = sizeof(*ed);
137 			biosdisk_getextinfo(d->dev, ed);
138 		}
139 	}
140 
141 	/*
142 	 * If the drive is 2.88MB floppy drive, check that we can actually
143 	 * read sector >= 18. If not, assume 1.44MB floppy disk.
144 	 */
145 	if (d->type == BIOSDISK_TYPE_FD && SPT(diskinfo) == 36) {
146 		char buf[512];
147 
148 		if (biosdisk_read(d->dev, 0, 0, 18, 1, buf)) {
149 			d->sec = 18;
150 			d->chs_sectors /= 2;
151 		}
152 	}
153 
154 	return 0;
155 }
156 
157 /*
158  * Global shared "diskbuf" is used as read ahead buffer.  For reading from
159  * floppies, the bootstrap has to be loaded on a 64K boundary to ensure that
160  * this buffer doesn't cross a 64K DMA boundary.
161  */
162 static int      ra_dev;
163 static int      ra_end;
164 static int      ra_first;
165 
166 /*
167  * Because some older BIOSes have bugs in their int13 extensions, we
168  * only try to use the extended read if the I/O request can't be addressed
169  * using CHS.
170  *
171  * Of course, some BIOSes have bugs in ths CHS read, such as failing to
172  * function properly if the MBR table has a different geometry than the
173  * BIOS would generate internally for the device in question, and so we
174  * provide a way to force the extended on hard disks via a compile-time
175  * option.
176  */
177 #if defined(FORCE_INT13EXT)
178 #define	NEED_INT13EXT(d, dblk, num)				\
179 	(((d)->dev & 0x80) != 0)
180 #else
181 #define	NEED_INT13EXT(d, dblk, num)				\
182 	(((d)->type == BIOSDISK_TYPE_CD) ||                     \
183 	 ((d)->type == BIOSDISK_TYPE_HD &&			\
184 	  ((dblk) + (num)) >= (d)->chs_sectors))
185 #endif
186 
187 static int
188 do_read(struct biosdisk_ll *d, daddr_t dblk, int num, char *buf)
189 {
190 
191 	if (NEED_INT13EXT(d, dblk, num)) {
192 		struct {
193 			int8_t size;
194 			int8_t resvd;
195 			int16_t cnt;
196 			int16_t off;
197 			int16_t seg;
198 			int64_t sec;
199 		} ext;
200 
201 		if (!(d->flags & BIOSDISK_INT13EXT))
202 			return -1;
203 		ext.size = sizeof(ext);
204 		ext.resvd = 0;
205 		ext.cnt = num;
206 		/* seg:off of physical address */
207 		ext.off = (int)buf & 0xf;
208 		ext.seg = vtophys(buf) >> 4;
209 		ext.sec = dblk;
210 
211 		if (biosdisk_extread(d->dev, &ext)) {
212 			(void)biosdisk_reset(d->dev);
213 			return -1;
214 		}
215 
216 		return ext.cnt;
217 	} else {
218 		int cyl, head, sec, nsec, spc, dblk32;
219 
220 		dblk32 = (int)dblk;
221 		spc = d->head * d->sec;
222 		cyl = dblk32 / spc;
223 		head = (dblk32 % spc) / d->sec;
224 		sec = dblk32 % d->sec;
225 		nsec = d->sec - sec;
226 
227 		if (nsec > num)
228 			nsec = num;
229 
230 		if (biosdisk_read(d->dev, cyl, head, sec, nsec, buf)) {
231 			(void)biosdisk_reset(d->dev);
232 			return -1;
233 		}
234 
235 		return nsec;
236 	}
237 }
238 
239 /*
240  * NB if 'cold' is set below not all of the program is loaded, so
241  * mustn't use data segment, bss, call library functions or do read-ahead.
242  */
243 int
244 readsects(struct biosdisk_ll *d, daddr_t dblk, int num, char *buf, int cold)
245 {
246 #ifdef BOOTXX
247 #define cold 1		/* collapse out references to diskbufp */
248 #endif
249 	while (num) {
250 		int nsec;
251 
252 		/* check for usable data in read-ahead buffer */
253 		if (cold || diskbuf_user != &ra_dev || d->dev != ra_dev
254 		    || dblk < ra_first || dblk >= ra_end) {
255 
256 			/* no, read from disk */
257 			char *trbuf;
258 			int maxsecs;
259 			int retries = BIOSDISK_RETRIES;
260 
261 			if (cold) {
262 				/* transfer directly to buffer */
263 				trbuf = buf;
264 				maxsecs = num;
265 			} else {
266 				/* fill read-ahead buffer */
267 				trbuf = alloc_diskbuf(0); /* no data yet */
268 				maxsecs = DISKBUFSIZE / d->secsize;
269 			}
270 
271 			while ((nsec = do_read(d, dblk, maxsecs, trbuf)) < 0) {
272 #ifdef DISK_DEBUG
273 				if (!cold)
274 					printf("read error dblk %d-%d\n", (int)dblk,
275 					       (int)(dblk + maxsecs - 1));
276 #endif
277 				if (--retries >= 0)
278 					continue;
279 				return -1;	/* XXX cannot output here if
280 						 * (cold) */
281 			}
282 			if (!cold) {
283 				ra_dev = d->dev;
284 				ra_first = dblk;
285 				ra_end = dblk + nsec;
286 				diskbuf_user = &ra_dev;
287 			}
288 		} else		/* can take blocks from end of read-ahead
289 				 * buffer */
290 			nsec = ra_end - dblk;
291 
292 		if (!cold) {
293 			/* copy data from read-ahead to user buffer */
294 			if (nsec > num)
295 				nsec = num;
296 			memcpy(buf,
297 			       diskbufp + (dblk - ra_first) * d->secsize,
298 			       nsec * d->secsize);
299 		}
300 		buf += nsec * d->secsize;
301 		num -= nsec;
302 		dblk += nsec;
303 	}
304 
305 	return 0;
306 }
307 
308 /*
309  * Return the number of hard disk drives.
310  */
311 int
312 get_harddrives(void)
313 {
314 	/*
315 	 * Some BIOSes are buggy so that they return incorrect number
316 	 * of hard drives with int13/ah=8. We read a byte at 0040:0075
317 	 * instead, which is known to be always correct.
318 	 */
319 	int n = 0;
320 
321 	pvbcopy((void *)0x475, &n, 1);
322 
323 	return n;
324 }
325