]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/pc98/libpc98/bioscd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / boot / 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 } bcinfo [MAXBCDEV];
88 static int nbcinfo = 0;
89
90 static int      bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
91 static int      bc_init(void);
92 static int      bc_strategy(void *devdata, int flag, daddr_t dblk,
93                     size_t size, char *buf, size_t *rsize);
94 static int      bc_open(struct open_file *f, ...);
95 static int      bc_close(struct open_file *f);
96 static void     bc_print(int verbose);
97
98 struct devsw bioscd = {
99         "cd", 
100         DEVT_CD, 
101         bc_init,
102         bc_strategy, 
103         bc_open, 
104         bc_close, 
105         noioctl,
106         bc_print,
107         NULL
108 };
109
110 /*
111  * Translate between BIOS device numbers and our private unit numbers.
112  */
113 int
114 bc_bios2unit(int biosdev)
115 {
116         int i;
117     
118         DEBUG("looking for bios device 0x%x", biosdev);
119         for (i = 0; i < nbcinfo; i++) {
120                 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
121                 if (bcinfo[i].bc_unit == biosdev)
122                         return(i);
123         }
124         return(-1);
125 }
126
127 int
128 bc_unit2bios(int unit)
129 {
130         if ((unit >= 0) && (unit < nbcinfo))
131                 return(bcinfo[unit].bc_unit);
132         return(-1);
133 }
134
135 /*    
136  * We can't quiz, we have to be told what device to use, so this functoin
137  * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
138  * device number to add.
139  */
140 static int
141 bc_init(void) 
142 {
143
144         return (0);
145 }
146
147 int
148 bc_add(int biosdev)
149 {
150
151         if (nbcinfo >= MAXBCDEV)
152                 return (-1);
153         bcinfo[nbcinfo].bc_unit = biosdev;
154
155         /* SCSI CD-ROM only */
156         if ((biosdev & 0xf0) != 0xa0)
157                 return (-1);
158         if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
159                 return (-1);
160
161         printf("BIOS CD is cd%d\n", nbcinfo);
162         nbcinfo++;
163         return(0);
164 }
165
166 /*
167  * Print information about disks
168  */
169 static void
170 bc_print(int verbose)
171 {
172         char line[80];
173         int i;
174
175         for (i = 0; i < nbcinfo; i++) {
176                 sprintf(line, "    cd%d: Device 0x%x\n", i,
177                     bcinfo[i].bc_sp.sp_devicespec);
178                 pager_output(line);
179         }
180 }
181
182 /*
183  * Attempt to open the disk described by (dev) for use by (f).
184  */
185 static int 
186 bc_open(struct open_file *f, ...)
187 {
188         va_list ap;
189         struct i386_devdesc *dev;
190
191         va_start(ap, f);
192         dev = va_arg(ap, struct i386_devdesc *);
193         va_end(ap);
194         if (dev->d_unit >= nbcinfo) {
195                 DEBUG("attempt to open nonexistent disk");
196                 return(ENXIO);
197         }
198
199         return(0);
200 }
201  
202 static int 
203 bc_close(struct open_file *f)
204 {
205
206         return(0);
207 }
208
209 static int 
210 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
211     size_t *rsize)
212 {
213         struct i386_devdesc *dev;
214         int unit;
215         int blks;
216 #ifdef BD_SUPPORT_FRAGS
217         char fragbuf[BIOSCD_SECSIZE];
218         size_t fragsize;
219
220         fragsize = size % BIOSCD_SECSIZE;
221 #else
222         if (size % BIOSCD_SECSIZE)
223                 return (EINVAL);
224 #endif
225
226         if (rw != F_READ)
227                 return(EROFS);
228         dev = (struct i386_devdesc *)devdata;
229         unit = dev->d_unit;
230         blks = size / BIOSCD_SECSIZE;
231         if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
232                 return (EINVAL);
233         dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
234         DEBUG("read %d from %lld to %p", blks, dblk, buf);
235
236         if (rsize)
237                 *rsize = 0;
238         if (blks && bc_read(unit, dblk, blks, buf)) {
239                 DEBUG("read error");
240                 return (EIO);
241         }
242 #ifdef BD_SUPPORT_FRAGS
243         DEBUG("frag read %d from %lld+%d to %p", 
244             fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
245         if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
246                 DEBUG("frag read error");
247                 return(EIO);
248         }
249         bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
250 #endif  
251         if (rsize)
252                 *rsize = size;
253         return (0);
254 }
255
256 /* Max number of sectors to bounce-buffer at a time. */
257 #define CD_BOUNCEBUF    8
258
259 static int
260 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
261 {
262         u_int maxfer, resid, result, retry, x;
263         caddr_t bbuf, p, xp;
264         int biosdev;
265 #ifdef DISK_DEBUG
266         int error;
267 #endif
268     
269         /* Just in case some idiot actually tries to read -1 blocks... */
270         if (blks < 0)
271                 return (-1);
272
273         /* If nothing to do, just return succcess. */
274         if (blks == 0)
275                 return (0);
276
277         /* Decide whether we have to bounce */
278         if (VTOP(dest) >> 20 != 0) {
279                 /* 
280                  * The destination buffer is above first 1MB of
281                  * physical memory so we have to arrange a suitable
282                  * bounce buffer.
283                  */
284                 x = min(CD_BOUNCEBUF, (unsigned)blks);
285                 bbuf = alloca(x * BIOSCD_SECSIZE);
286                 maxfer = x;
287         } else {
288                 bbuf = NULL;
289                 maxfer = 0;
290         }
291         
292         biosdev = bc_unit2bios(unit);
293         resid = blks;
294         p = dest;
295
296         while (resid > 0) {
297                 if (bbuf)
298                         xp = bbuf;
299                 else
300                         xp = p;
301                 x = resid;
302                 if (maxfer > 0)
303                         x = min(x, maxfer);
304
305                 /*
306                  * Loop retrying the operation a couple of times.  The BIOS
307                  * may also retry.
308                  */
309                 for (retry = 0; retry < 3; retry++) {
310                         /* If retrying, reset the drive */
311                         if (retry > 0) {
312                                 v86.ctl = V86_FLAGS;
313                                 v86.addr = 0x1b;
314                                 v86.eax = 0x0300 | biosdev;
315                                 v86int();
316                         }
317
318                         v86.ctl = V86_FLAGS;
319                         v86.addr = 0x1b;
320                         v86.eax = 0x0600 | (biosdev & 0x7f);
321                         v86.ebx = x * BIOSCD_SECSIZE;
322                         v86.ecx = dblk & 0xffff;
323                         v86.edx = (dblk >> 16) & 0xffff;
324                         v86.ebp = VTOPOFF(xp);
325                         v86.es = VTOPSEG(xp);
326                         v86int();
327                         result = V86_CY(v86.efl);
328                         if (result == 0)
329                                 break;
330                 }
331         
332 #ifdef DISK_DEBUG
333                 error = (v86.eax >> 8) & 0xff;
334 #endif
335                 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
336                     VTOP(p), result ? "failed" : "ok");
337                 DEBUG("unit %d  status 0x%x", unit, error);
338                 if (bbuf != NULL)
339                         bcopy(bbuf, p, x * BIOSCD_SECSIZE);
340                 p += (x * BIOSCD_SECSIZE);
341                 dblk += x;
342                 resid -= x;
343         }
344         
345 /*      hexdump(dest, (blks * BIOSCD_SECSIZE)); */
346         return(0);
347 }
348
349 /*
350  * Return a suitable dev_t value for (dev).
351  */
352 int
353 bc_getdev(struct i386_devdesc *dev)
354 {
355     int biosdev, unit, device;
356     int major;
357     int rootdev;
358
359     unit = dev->d_unit;
360     biosdev = bc_unit2bios(unit);
361     DEBUG("unit %d BIOS device %d", unit, biosdev);
362     if (biosdev == -1)                          /* not a BIOS device */
363         return(-1);
364
365     device = biosdev & 0xf0;
366     if (device == 0x80)
367         major = ACDMAJOR;
368     else if (device == 0xa0)
369         major = CDMAJOR;
370     else
371         return (-1);
372
373     unit = 0;   /* XXX */
374
375     /* XXX: Assume partition 'a'. */
376     rootdev = MAKEBOOTDEV(major, 0, unit, 0);
377     DEBUG("dev is 0x%x\n", rootdev);
378     return(rootdev);
379 }