]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/pc98/libpc98/bioscd.c
MFC r325834,r325997,326502: Move sys/boot to stand/
[FreeBSD/FreeBSD.git] / stand / pc98 / libpc98 / bioscd.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * BIOS CD device handling for CD's that have been booted off of via no
33  * emulation booting as defined in the El Torito standard.
34  * 
35  * Ideas and algorithms from:
36  *
37  * - FreeBSD libi386/biosdisk.c
38  *
39  */
40
41 #include <stand.h>
42
43 #include <sys/param.h>
44 #include <machine/bootinfo.h>
45
46 #include <stdarg.h>
47
48 #include <bootstrap.h>
49 #include <btxv86.h>
50 #include "libi386.h"
51
52 #define BIOSCD_SECSIZE          2048
53 #define BUFSIZE                 (1 * BIOSCD_SECSIZE)
54 #define MAXBCDEV                1
55
56 /* Major numbers for devices we frontend for. */
57 #define ACDMAJOR                117
58 #define CDMAJOR                 15
59
60 #ifdef DISK_DEBUG
61 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
62 #else
63 # define DEBUG(fmt, args...)
64 #endif
65
66 struct specification_packet {
67         u_char  sp_size;
68         u_char  sp_bootmedia;
69         u_char  sp_drive;
70         u_char  sp_controller;
71         u_int   sp_lba;
72         u_short sp_devicespec;
73         u_short sp_buffersegment;
74         u_short sp_loadsegment;
75         u_short sp_sectorcount;
76         u_short sp_cylsec;
77         u_char  sp_head;
78 };
79
80 /*
81  * List of BIOS devices, translation from disk unit number to
82  * BIOS unit number.
83  */
84 static struct bcinfo {
85         int     bc_unit;                /* BIOS unit number */
86         struct specification_packet bc_sp;
87         int     bc_open;                /* reference counter */
88         void    *bc_bcache;             /* buffer cache data */
89 } bcinfo [MAXBCDEV];
90 static int nbcinfo = 0;
91
92 #define BC(dev) (bcinfo[(dev)->d_unit])
93
94 static int      bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
95 static int      bc_init(void);
96 static int      bc_strategy(void *devdata, int flag, daddr_t dblk,
97                     size_t size, char *buf, size_t *rsize);
98 static int      bc_realstrategy(void *devdata, int flag, daddr_t dblk,
99                     size_t size, char *buf, size_t *rsize);
100 static int      bc_open(struct open_file *f, ...);
101 static int      bc_close(struct open_file *f);
102 static int      bc_print(int verbose);
103
104 struct devsw bioscd = {
105         "cd", 
106         DEVT_CD, 
107         bc_init,
108         bc_strategy, 
109         bc_open, 
110         bc_close, 
111         noioctl,
112         bc_print,
113         NULL
114 };
115
116 /*
117  * Translate between BIOS device numbers and our private unit numbers.
118  */
119 int
120 bc_bios2unit(int biosdev)
121 {
122         int i;
123     
124         DEBUG("looking for bios device 0x%x", biosdev);
125         for (i = 0; i < nbcinfo; i++) {
126                 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
127                 if (bcinfo[i].bc_unit == biosdev)
128                         return(i);
129         }
130         return(-1);
131 }
132
133 int
134 bc_unit2bios(int unit)
135 {
136         if ((unit >= 0) && (unit < nbcinfo))
137                 return(bcinfo[unit].bc_unit);
138         return(-1);
139 }
140
141 /*    
142  * We can't quiz, we have to be told what device to use, so this functoin
143  * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
144  * device number to add.
145  */
146 static int
147 bc_init(void) 
148 {
149
150         return (0);
151 }
152
153 int
154 bc_add(int biosdev)
155 {
156
157         if (nbcinfo >= MAXBCDEV)
158                 return (-1);
159         bcinfo[nbcinfo].bc_unit = biosdev;
160
161         /* SCSI CD-ROM only */
162         if ((biosdev & 0xf0) != 0xa0)
163                 return (-1);
164         if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
165                 return (-1);
166
167         printf("BIOS CD is cd%d\n", nbcinfo);
168         nbcinfo++;
169         bcache_add_dev(nbcinfo);        /* register cd device in bcache */
170         return(0);
171 }
172
173 /*
174  * Print information about disks
175  */
176 static int
177 bc_print(int verbose)
178 {
179         char line[80];
180         int i, ret = 0;
181
182         if (nbcinfo == 0)
183                 return (0);
184
185         printf("%s devices:", bioscd.dv_name);
186         if ((ret = pager_output("\n")) != 0)
187                 return (ret);
188
189         for (i = 0; i < nbcinfo; i++) {
190                 sprintf(line, "    cd%d: Device 0x%x\n", i,
191                     bcinfo[i].bc_sp.sp_devicespec);
192                 if ((ret = pager_output(line)) != 0)
193                         break;
194         }
195         return (ret);
196 }
197
198 /*
199  * Attempt to open the disk described by (dev) for use by (f).
200  */
201 static int 
202 bc_open(struct open_file *f, ...)
203 {
204         va_list ap;
205         struct i386_devdesc *dev;
206
207         va_start(ap, f);
208         dev = va_arg(ap, struct i386_devdesc *);
209         va_end(ap);
210         if (dev->d_unit >= nbcinfo) {
211                 DEBUG("attempt to open nonexistent disk");
212                 return(ENXIO);
213         }
214
215         BC(dev).bc_open++;
216         if (BC(dev).bc_bcache == NULL)
217                 BC(dev).bc_bcache = bcache_allocate();
218         return(0);
219 }
220  
221 static int 
222 bc_close(struct open_file *f)
223 {
224         struct i386_devdesc *dev;
225
226         dev = (struct i386_devdesc *)f->f_devdata;
227         BC(dev).bc_open--;
228         if (BC(dev).bc_open == 0) {
229                 bcache_free(BC(dev).bc_bcache);
230                 BC(dev).bc_bcache = NULL;
231         }
232         return(0);
233 }
234
235 static int
236 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
237     char *buf, size_t *rsize)
238 {
239         struct bcache_devdata bcd;
240         struct i386_devdesc *dev;
241
242         dev = (struct i386_devdesc *)devdata;
243         bcd.dv_strategy = bc_realstrategy;
244         bcd.dv_devdata = devdata;
245         bcd.dv_cache = BC(dev).bc_bcache;
246
247         return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
248 }
249
250 static int 
251 bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
252     char *buf, size_t *rsize)
253 {
254         struct i386_devdesc *dev;
255         int unit;
256         int blks;
257 #ifdef BD_SUPPORT_FRAGS
258         char fragbuf[BIOSCD_SECSIZE];
259         size_t fragsize;
260
261         fragsize = size % BIOSCD_SECSIZE;
262 #else
263         if (size % BIOSCD_SECSIZE)
264                 return (EINVAL);
265 #endif
266
267         if (rw != F_READ)
268                 return(EROFS);
269         dev = (struct i386_devdesc *)devdata;
270         unit = dev->d_unit;
271         blks = size / BIOSCD_SECSIZE;
272         if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
273                 return (EINVAL);
274         dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
275         DEBUG("read %d from %lld to %p", blks, dblk, buf);
276
277         if (rsize)
278                 *rsize = 0;
279         if (blks && bc_read(unit, dblk, blks, buf)) {
280                 DEBUG("read error");
281                 return (EIO);
282         }
283 #ifdef BD_SUPPORT_FRAGS
284         DEBUG("frag read %d from %lld+%d to %p", 
285             fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
286         if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
287                 DEBUG("frag read error");
288                 return(EIO);
289         }
290         bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
291 #endif  
292         if (rsize)
293                 *rsize = size;
294         return (0);
295 }
296
297 /* Max number of sectors to bounce-buffer at a time. */
298 #define CD_BOUNCEBUF    8
299
300 static int
301 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
302 {
303         u_int maxfer, resid, result, retry, x;
304         caddr_t bbuf, p, xp;
305         int biosdev;
306 #ifdef DISK_DEBUG
307         int error;
308 #endif
309     
310         /* Just in case some idiot actually tries to read -1 blocks... */
311         if (blks < 0)
312                 return (-1);
313
314         /* If nothing to do, just return succcess. */
315         if (blks == 0)
316                 return (0);
317
318         /* Decide whether we have to bounce */
319         if (VTOP(dest) >> 20 != 0) {
320                 /* 
321                  * The destination buffer is above first 1MB of
322                  * physical memory so we have to arrange a suitable
323                  * bounce buffer.
324                  */
325                 x = min(CD_BOUNCEBUF, (unsigned)blks);
326                 bbuf = alloca(x * BIOSCD_SECSIZE);
327                 maxfer = x;
328         } else {
329                 bbuf = NULL;
330                 maxfer = 0;
331         }
332         
333         biosdev = bc_unit2bios(unit);
334         resid = blks;
335         p = dest;
336
337         while (resid > 0) {
338                 if (bbuf)
339                         xp = bbuf;
340                 else
341                         xp = p;
342                 x = resid;
343                 if (maxfer > 0)
344                         x = min(x, maxfer);
345
346                 /*
347                  * Loop retrying the operation a couple of times.  The BIOS
348                  * may also retry.
349                  */
350                 for (retry = 0; retry < 3; retry++) {
351                         /* If retrying, reset the drive */
352                         if (retry > 0) {
353                                 v86.ctl = V86_FLAGS;
354                                 v86.addr = 0x1b;
355                                 v86.eax = 0x0300 | biosdev;
356                                 v86int();
357                         }
358
359                         v86.ctl = V86_FLAGS;
360                         v86.addr = 0x1b;
361                         v86.eax = 0x0600 | (biosdev & 0x7f);
362                         v86.ebx = x * BIOSCD_SECSIZE;
363                         v86.ecx = dblk & 0xffff;
364                         v86.edx = (dblk >> 16) & 0xffff;
365                         v86.ebp = VTOPOFF(xp);
366                         v86.es = VTOPSEG(xp);
367                         v86int();
368                         result = V86_CY(v86.efl);
369                         if (result == 0)
370                                 break;
371                 }
372         
373 #ifdef DISK_DEBUG
374                 error = (v86.eax >> 8) & 0xff;
375 #endif
376                 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
377                     VTOP(p), result ? "failed" : "ok");
378                 DEBUG("unit %d  status 0x%x", unit, error);
379                 if (bbuf != NULL)
380                         bcopy(bbuf, p, x * BIOSCD_SECSIZE);
381                 p += (x * BIOSCD_SECSIZE);
382                 dblk += x;
383                 resid -= x;
384         }
385         
386 /*      hexdump(dest, (blks * BIOSCD_SECSIZE)); */
387         return(0);
388 }
389
390 /*
391  * Return a suitable dev_t value for (dev).
392  */
393 int
394 bc_getdev(struct i386_devdesc *dev)
395 {
396     int biosdev, unit, device;
397     int major;
398     int rootdev;
399
400     unit = dev->d_unit;
401     biosdev = bc_unit2bios(unit);
402     DEBUG("unit %d BIOS device %d", unit, biosdev);
403     if (biosdev == -1)                          /* not a BIOS device */
404         return(-1);
405
406     device = biosdev & 0xf0;
407     if (device == 0x80)
408         major = ACDMAJOR;
409     else if (device == 0xa0)
410         major = CDMAJOR;
411     else
412         return (-1);
413
414     unit = 0;   /* XXX */
415
416     /* XXX: Assume partition 'a'. */
417     rootdev = MAKEBOOTDEV(major, 0, unit, 0);
418     DEBUG("dev is 0x%x\n", rootdev);
419     return(rootdev);
420 }