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