xref: /netbsd-src/sys/arch/i386/stand/lib/biosdisk_ll.c (revision 410a658a54997140435f9b643f57110fcff2dc43)
1*410a658aSjakllsch /*	$NetBSD: biosdisk_ll.c,v 1.31 2011/02/21 02:58:02 jakllsch Exp $	 */
2c53f251eSjunyoung 
3c53f251eSjunyoung /*-
4c53f251eSjunyoung  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5c53f251eSjunyoung  * All rights reserved.
6c53f251eSjunyoung  *
7c53f251eSjunyoung  * This code is derived from software contributed to The NetBSD Foundation
8c53f251eSjunyoung  * by Bang Jun-Young.
9c53f251eSjunyoung  *
10c53f251eSjunyoung  * Redistribution and use in source and binary forms, with or without
11c53f251eSjunyoung  * modification, are permitted provided that the following conditions
12c53f251eSjunyoung  * are met:
13c53f251eSjunyoung  * 1. Redistributions of source code must retain the above copyright
14c53f251eSjunyoung  *    notice, this list of conditions and the following disclaimer.
15c53f251eSjunyoung  * 2. Redistributions in binary form must reproduce the above copyright
16c53f251eSjunyoung  *    notice, this list of conditions and the following disclaimer in the
17c53f251eSjunyoung  *    documentation and/or other materials provided with the distribution.
18c53f251eSjunyoung  *
19c53f251eSjunyoung  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20c53f251eSjunyoung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21c53f251eSjunyoung  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22c53f251eSjunyoung  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23c53f251eSjunyoung  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24c53f251eSjunyoung  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25c53f251eSjunyoung  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26c53f251eSjunyoung  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27c53f251eSjunyoung  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28c53f251eSjunyoung  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29c53f251eSjunyoung  * POSSIBILITY OF SUCH DAMAGE.
30c53f251eSjunyoung  */
31816bb961Sperry 
32816bb961Sperry /*
33816bb961Sperry  * Copyright (c) 1996
34816bb961Sperry  * 	Matthias Drochner.  All rights reserved.
35816bb961Sperry  * Copyright (c) 1996
36816bb961Sperry  * 	Perry E. Metzger.  All rights reserved.
37816bb961Sperry  *
38816bb961Sperry  * Redistribution and use in source and binary forms, with or without
39816bb961Sperry  * modification, are permitted provided that the following conditions
40816bb961Sperry  * are met:
41816bb961Sperry  * 1. Redistributions of source code must retain the above copyright
42816bb961Sperry  *    notice, this list of conditions and the following disclaimer.
43816bb961Sperry  * 2. Redistributions in binary form must reproduce the above copyright
44816bb961Sperry  *    notice, this list of conditions and the following disclaimer in the
45816bb961Sperry  *    documentation and/or other materials provided with the distribution.
46816bb961Sperry  * 3. All advertising materials mentioning features or use of this software
47816bb961Sperry  *    must display the following acknowledgements:
48816bb961Sperry  *	This product includes software developed for the NetBSD Project
49816bb961Sperry  *	by Matthias Drochner.
50816bb961Sperry  *	This product includes software developed for the NetBSD Project
51816bb961Sperry  *	by Perry E. Metzger.
52816bb961Sperry  * 4. The names of the authors may not be used to endorse or promote products
53816bb961Sperry  *    derived from this software without specific prior written permission.
54816bb961Sperry  *
55816bb961Sperry  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
56816bb961Sperry  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
57816bb961Sperry  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
58816bb961Sperry  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
59816bb961Sperry  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
60816bb961Sperry  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
61816bb961Sperry  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
62816bb961Sperry  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63816bb961Sperry  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
64816bb961Sperry  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65816bb961Sperry  */
66816bb961Sperry 
675e82fdddSthorpej /*
68d55d9adcSdrochner  * shared by bootsector startup (bootsectmain) and biosdisk.c
69d55d9adcSdrochner  * needs lowlevel parts from bios_disk.S
70816bb961Sperry  */
71816bb961Sperry 
720b61932bSjakllsch #include <lib/libkern/libkern.h>
73816bb961Sperry #include <lib/libsa/stand.h>
74816bb961Sperry 
75816bb961Sperry #include "biosdisk_ll.h"
76816bb961Sperry #include "diskbuf.h"
77b13abba3Sjunyoung #include "libi386.h"
78816bb961Sperry 
79fd18f392Sdsl static int do_read(struct biosdisk_ll *, daddr_t, int, char *);
80816bb961Sperry 
8162031ff9Sdrochner /*
8262031ff9Sdrochner  * we get from get_diskinfo():
83beeb6c87Sjunyoung  *      %ah      %ch      %cl      %dh (registers after int13/8), ie
84c53f251eSjunyoung  * xxxxxxxx cccccccc CCssssss hhhhhhhh
8562031ff9Sdrochner  */
86beeb6c87Sjunyoung #define STATUS(di)	((di)>>24)
8762031ff9Sdrochner #define SPT(di)		(((di)>>8)&0x3f)
8862031ff9Sdrochner #define HEADS(di)	(((di)&0xff)+1)
8962031ff9Sdrochner #define CYL(di)		(((((di)>>16)&0xff)|(((di)>>6)&0x300))+1)
90816bb961Sperry 
917cdf5d9aSdrochner #ifndef BIOSDISK_RETRIES
927cdf5d9aSdrochner #define BIOSDISK_RETRIES 5
937cdf5d9aSdrochner #endif
947cdf5d9aSdrochner 
955e82fdddSthorpej int
set_geometry(struct biosdisk_ll * d,struct biosdisk_extinfo * ed)96f10ef234Sjunyoung set_geometry(struct biosdisk_ll *d, struct biosdisk_extinfo *ed)
97816bb961Sperry {
9862031ff9Sdrochner 	int diskinfo;
99816bb961Sperry 
100f10ef234Sjunyoung 	diskinfo = biosdisk_getinfo(d->dev);
10162031ff9Sdrochner 	d->sec = SPT(diskinfo);
10262031ff9Sdrochner 	d->head = HEADS(diskinfo);
10362031ff9Sdrochner 	d->cyl = CYL(diskinfo);
10448003b2cSfvdl 	d->chs_sectors = d->sec * d->head * d->cyl;
105816bb961Sperry 
106c53f251eSjunyoung 	if (d->dev >= 0x80 + get_harddrives()) {
107c53f251eSjunyoung 		d->secsize = 2048;
108c53f251eSjunyoung 		d->type = BIOSDISK_TYPE_CD;
109c53f251eSjunyoung 	} else {
110c53f251eSjunyoung 		d->secsize = 512;
111c53f251eSjunyoung 		if (d->dev & 0x80)
112c53f251eSjunyoung 			d->type = BIOSDISK_TYPE_HD;
113c53f251eSjunyoung 		else
114c53f251eSjunyoung 			d->type = BIOSDISK_TYPE_FD;
115c53f251eSjunyoung 	}
116c53f251eSjunyoung 
117c53f251eSjunyoung 	/*
118c53f251eSjunyoung 	 * Some broken BIOSes such as one found on Soltek SL-75DRV2 report
119c53f251eSjunyoung 	 * that they don't support int13 extension for CD-ROM drives while
120c53f251eSjunyoung 	 * they actually do. As a workaround, if the boot device is a CD we
121c53f251eSjunyoung 	 * assume that the extension is available. Note that only very old
122c53f251eSjunyoung 	 * BIOSes don't support the extended mode, and they don't work with
123c53f251eSjunyoung 	 * ATAPI CD-ROM drives, either. So there's no problem.
124c53f251eSjunyoung 	 */
125f82918b4Sws 	d->flags = 0;
126c53f251eSjunyoung 	if (d->type == BIOSDISK_TYPE_CD ||
127f10ef234Sjunyoung 	    (d->type == BIOSDISK_TYPE_HD && biosdisk_int13ext(d->dev))) {
128f10ef234Sjunyoung 		d->flags |= BIOSDISK_INT13EXT;
129fd18f392Sdsl 		if (ed != NULL) {
130c53f251eSjunyoung 			ed->size = sizeof(*ed);
131937d3ce3Sdyoung 			if (biosdisk_getextinfo(d->dev, ed) != 0)
132*410a658aSjakllsch 				return -1;
133f8e22151Sfvdl 		}
134fd18f392Sdsl 	}
135816bb961Sperry 
1365e82fdddSthorpej 	/*
1377afdc17eSjunyoung 	 * If the drive is 2.88MB floppy drive, check that we can actually
1387afdc17eSjunyoung 	 * read sector >= 18. If not, assume 1.44MB floppy disk.
1396227201bSjdolecek 	 */
140c53f251eSjunyoung 	if (d->type == BIOSDISK_TYPE_FD && SPT(diskinfo) == 36) {
141c53f251eSjunyoung 		char buf[512];
142c53f251eSjunyoung 
143f10ef234Sjunyoung 		if (biosdisk_read(d->dev, 0, 0, 18, 1, buf)) {
1446227201bSjdolecek 			d->sec = 18;
1456227201bSjdolecek 			d->chs_sectors /= 2;
1466227201bSjdolecek 		}
1476227201bSjdolecek 	}
1486227201bSjdolecek 
149c53f251eSjunyoung 	return 0;
150816bb961Sperry }
151816bb961Sperry 
1525e82fdddSthorpej /*
1535e82fdddSthorpej  * Global shared "diskbuf" is used as read ahead buffer.  For reading from
1545e82fdddSthorpej  * floppies, the bootstrap has to be loaded on a 64K boundary to ensure that
1555e82fdddSthorpej  * this buffer doesn't cross a 64K DMA boundary.
156816bb961Sperry  */
157816bb961Sperry static int      ra_dev;
15888846082Sjakllsch static daddr_t  ra_end;
15988846082Sjakllsch static daddr_t  ra_first;
160816bb961Sperry 
16174010475Sthorpej /*
16274010475Sthorpej  * Because some older BIOSes have bugs in their int13 extensions, we
16374010475Sthorpej  * only try to use the extended read if the I/O request can't be addressed
16474010475Sthorpej  * using CHS.
16574010475Sthorpej  *
16674010475Sthorpej  * Of course, some BIOSes have bugs in ths CHS read, such as failing to
16774010475Sthorpej  * function properly if the MBR table has a different geometry than the
16874010475Sthorpej  * BIOS would generate internally for the device in question, and so we
16974010475Sthorpej  * provide a way to force the extended on hard disks via a compile-time
17074010475Sthorpej  * option.
17174010475Sthorpej  */
17274010475Sthorpej #if defined(FORCE_INT13EXT)
17374010475Sthorpej #define	NEED_INT13EXT(d, dblk, num)				\
17474010475Sthorpej 	(((d)->dev & 0x80) != 0)
17574010475Sthorpej #else
17674010475Sthorpej #define	NEED_INT13EXT(d, dblk, num)				\
177c53f251eSjunyoung 	(((d)->type == BIOSDISK_TYPE_CD) ||                     \
178c53f251eSjunyoung 	 ((d)->type == BIOSDISK_TYPE_HD &&			\
179c53f251eSjunyoung 	  ((dblk) + (num)) >= (d)->chs_sectors))
18074010475Sthorpej #endif
18174010475Sthorpej 
182f82918b4Sws static int
do_read(struct biosdisk_ll * d,daddr_t dblk,int num,char * buf)183fd18f392Sdsl do_read(struct biosdisk_ll *d, daddr_t dblk, int num, char *buf)
184f82918b4Sws {
185c53f251eSjunyoung 
186c53f251eSjunyoung 	if (NEED_INT13EXT(d, dblk, num)) {
187f82918b4Sws 		struct {
188f82918b4Sws 			int8_t size;
189f82918b4Sws 			int8_t resvd;
190f82918b4Sws 			int16_t cnt;
191f82918b4Sws 			int16_t off;
192f82918b4Sws 			int16_t seg;
193f82918b4Sws 			int64_t sec;
194f82918b4Sws 		} ext;
195f82918b4Sws 
196f10ef234Sjunyoung 		if (!(d->flags & BIOSDISK_INT13EXT))
19748003b2cSfvdl 			return -1;
198f82918b4Sws 		ext.size = sizeof(ext);
199f82918b4Sws 		ext.resvd = 0;
200f82918b4Sws 		ext.cnt = num;
20181acacd7Sdsl 		/* seg:off of physical address */
20281acacd7Sdsl 		ext.off = (int)buf & 0xf;
20381acacd7Sdsl 		ext.seg = vtophys(buf) >> 4;
204f82918b4Sws 		ext.sec = dblk;
205f82918b4Sws 
206f10ef234Sjunyoung 		if (biosdisk_extread(d->dev, &ext)) {
207f10ef234Sjunyoung 			(void)biosdisk_reset(d->dev);
208f82918b4Sws 			return -1;
209e57ba8d6Sdyoung 		}
210f82918b4Sws 
211f82918b4Sws 		return ext.cnt;
212f82918b4Sws 	} else {
213c53f251eSjunyoung 		int cyl, head, sec, nsec, spc, dblk32;
214c53f251eSjunyoung 
21542614ed3Sfvdl 		dblk32 = (int)dblk;
21662031ff9Sdrochner 		spc = d->head * d->sec;
21742614ed3Sfvdl 		cyl = dblk32 / spc;
21842614ed3Sfvdl 		head = (dblk32 % spc) / d->sec;
21942614ed3Sfvdl 		sec = dblk32 % d->sec;
220f8e22151Sfvdl 		nsec = d->sec - sec;
221f82918b4Sws 
222f82918b4Sws 		if (nsec > num)
223f82918b4Sws 			nsec = num;
224f82918b4Sws 
225f10ef234Sjunyoung 		if (biosdisk_read(d->dev, cyl, head, sec, nsec, buf)) {
226f10ef234Sjunyoung 			(void)biosdisk_reset(d->dev);
227f82918b4Sws 			return -1;
228e57ba8d6Sdyoung 		}
229f82918b4Sws 
230f82918b4Sws 		return nsec;
231f82918b4Sws 	}
232f82918b4Sws }
233f82918b4Sws 
234c53f251eSjunyoung /*
235c53f251eSjunyoung  * NB if 'cold' is set below not all of the program is loaded, so
236c53f251eSjunyoung  * mustn't use data segment, bss, call library functions or do read-ahead.
23781acacd7Sdsl  */
2385e82fdddSthorpej int
readsects(struct biosdisk_ll * d,daddr_t dblk,int num,char * buf,int cold)239fd18f392Sdsl readsects(struct biosdisk_ll *d, daddr_t dblk, int num, char *buf, int cold)
240816bb961Sperry {
241fd18f392Sdsl #ifdef BOOTXX
242fd18f392Sdsl #define cold 1		/* collapse out references to diskbufp */
243fd18f392Sdsl #endif
244816bb961Sperry 	while (num) {
245816bb961Sperry 		int nsec;
246816bb961Sperry 
247816bb961Sperry 		/* check for usable data in read-ahead buffer */
248816bb961Sperry 		if (cold || diskbuf_user != &ra_dev || d->dev != ra_dev
249816bb961Sperry 		    || dblk < ra_first || dblk >= ra_end) {
250816bb961Sperry 
251816bb961Sperry 			/* no, read from disk */
252816bb961Sperry 			char *trbuf;
25362031ff9Sdrochner 			int maxsecs;
2547cdf5d9aSdrochner 			int retries = BIOSDISK_RETRIES;
255816bb961Sperry 
256816bb961Sperry 			if (cold) {
257816bb961Sperry 				/* transfer directly to buffer */
258816bb961Sperry 				trbuf = buf;
25962031ff9Sdrochner 				maxsecs = num;
260816bb961Sperry 			} else {
261816bb961Sperry 				/* fill read-ahead buffer */
26281acacd7Sdsl 				trbuf = alloc_diskbuf(0); /* no data yet */
263c53f251eSjunyoung 				maxsecs = DISKBUFSIZE / d->secsize;
264816bb961Sperry 			}
265816bb961Sperry 
26662031ff9Sdrochner 			while ((nsec = do_read(d, dblk, maxsecs, trbuf)) < 0) {
2677cdf5d9aSdrochner #ifdef DISK_DEBUG
2687cdf5d9aSdrochner 				if (!cold)
2690b61932bSjakllsch 					printf("read error dblk %"PRId64"-%"PRId64"\n",
27088846082Sjakllsch 					    dblk, (dblk + maxsecs - 1));
2717cdf5d9aSdrochner #endif
272f82918b4Sws 				if (--retries >= 0)
2737cdf5d9aSdrochner 					continue;
2747afdc17eSjunyoung 				return -1;	/* XXX cannot output here if
2755e82fdddSthorpej 						 * (cold) */
276816bb961Sperry 			}
277f82918b4Sws 			if (!cold) {
278f82918b4Sws 				ra_dev = d->dev;
279f82918b4Sws 				ra_first = dblk;
280f82918b4Sws 				ra_end = dblk + nsec;
281f82918b4Sws 				diskbuf_user = &ra_dev;
282f82918b4Sws 			}
2835e82fdddSthorpej 		} else		/* can take blocks from end of read-ahead
2845e82fdddSthorpej 				 * buffer */
285816bb961Sperry 			nsec = ra_end - dblk;
286816bb961Sperry 
287816bb961Sperry 		if (!cold) {
288816bb961Sperry 			/* copy data from read-ahead to user buffer */
2895e82fdddSthorpej 			if (nsec > num)
2905e82fdddSthorpej 				nsec = num;
291c3748d91Sperry 			memcpy(buf,
292c53f251eSjunyoung 			       diskbufp + (dblk - ra_first) * d->secsize,
293c53f251eSjunyoung 			       nsec * d->secsize);
294816bb961Sperry 		}
295c53f251eSjunyoung 		buf += nsec * d->secsize;
296816bb961Sperry 		num -= nsec;
297816bb961Sperry 		dblk += nsec;
298816bb961Sperry 	}
299816bb961Sperry 
3007afdc17eSjunyoung 	return 0;
301816bb961Sperry }
302c53f251eSjunyoung 
303c53f251eSjunyoung /*
304c53f251eSjunyoung  * Return the number of hard disk drives.
305c53f251eSjunyoung  */
306c53f251eSjunyoung int
get_harddrives(void)307c53f251eSjunyoung get_harddrives(void)
308c53f251eSjunyoung {
309c53f251eSjunyoung 	/*
310c53f251eSjunyoung 	 * Some BIOSes are buggy so that they return incorrect number
311c53f251eSjunyoung 	 * of hard drives with int13/ah=8. We read a byte at 0040:0075
312c53f251eSjunyoung 	 * instead, which is known to be always correct.
313c53f251eSjunyoung 	 */
314c53f251eSjunyoung 	int n = 0;
315c53f251eSjunyoung 
316c53f251eSjunyoung 	pvbcopy((void *)0x475, &n, 1);
317c53f251eSjunyoung 
318c53f251eSjunyoung 	return n;
319c53f251eSjunyoung }
320