]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/boot/i386/libi386/bioscd.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / boot / i386 / libi386 / 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 #include <machine/psl.h>
46
47 #include <stdarg.h>
48
49 #include <bootstrap.h>
50 #include <btxv86.h>
51 #include <edd.h>
52 #include "libi386.h"
53
54 #define BIOSCD_SECSIZE          2048
55 #define BUFSIZE                 (1 * BIOSCD_SECSIZE)
56 #define MAXBCDEV                1
57
58 /* Major numbers for devices we frontend for. */
59 #define ACDMAJOR                117
60 #define CDMAJOR                 15
61
62 #ifdef DISK_DEBUG
63 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
64 #else
65 # define DEBUG(fmt, args...)
66 #endif
67
68 struct specification_packet {
69         u_char  sp_size;
70         u_char  sp_bootmedia;
71         u_char  sp_drive;
72         u_char  sp_controller;
73         u_int   sp_lba;
74         u_short sp_devicespec;
75         u_short sp_buffersegment;
76         u_short sp_loadsegment;
77         u_short sp_sectorcount;
78         u_short sp_cylsec;
79         u_char  sp_head;
80 };
81
82 /*
83  * List of BIOS devices, translation from disk unit number to
84  * BIOS unit number.
85  */
86 static struct bcinfo {
87         int     bc_unit;                /* BIOS unit number */
88         struct specification_packet bc_sp;
89 } bcinfo [MAXBCDEV];
90 static int nbcinfo = 0;
91
92 static int      bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
93 static int      bc_init(void);
94 static int      bc_strategy(void *devdata, int flag, daddr_t dblk,
95                     size_t size, char *buf, size_t *rsize);
96 static int      bc_open(struct open_file *f, ...);
97 static int      bc_close(struct open_file *f);
98 static void     bc_print(int verbose);
99
100 struct devsw bioscd = {
101         "cd", 
102         DEVT_CD, 
103         bc_init,
104         bc_strategy, 
105         bc_open, 
106         bc_close, 
107         noioctl,
108         bc_print,
109         NULL
110 };
111
112 /*
113  * Translate between BIOS device numbers and our private unit numbers.
114  */
115 int
116 bc_bios2unit(int biosdev)
117 {
118         int i;
119     
120         DEBUG("looking for bios device 0x%x", biosdev);
121         printf("looking for bios device 0x%x, nbcinfo=%d\n", biosdev, nbcinfo);
122         for (i = 0; i < nbcinfo; i++) {
123                 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
124                 if (bcinfo[i].bc_unit == biosdev)
125                         return(i);
126         }
127         return(-1);
128 }
129
130 int
131 bc_unit2bios(int unit)
132 {
133         if ((unit >= 0) && (unit < nbcinfo))
134                 return(bcinfo[unit].bc_unit);
135         return(-1);
136 }
137
138 /*    
139  * We can't quiz, we have to be told what device to use, so this functoin
140  * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
141  * device number to add.
142  */
143 static int
144 bc_init(void) 
145 {
146
147         return (0);
148 }
149
150 int
151 bc_add(int biosdev)
152 {
153         printf("bc_add(%d)\n", biosdev);
154
155         if (nbcinfo >= MAXBCDEV)
156                 return (-1);
157         bcinfo[nbcinfo].bc_unit = biosdev;
158         v86.ctl = V86_FLAGS;
159         v86.addr = 0x13;
160         v86.eax = 0x4b01;
161         v86.edx = biosdev;
162         v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
163         v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
164         v86int();
165         if ((v86.eax & 0xff00) != 0) {
166                 printf("CD probe failed, eax=0x%08x\n", v86.eax);
167                 return (-1);
168         }
169
170         printf("BIOS CD is cd%d\n", nbcinfo);
171         nbcinfo++;
172         return(0);
173 }
174
175 /*
176  * Print information about disks
177  */
178 static void
179 bc_print(int verbose)
180 {
181         char line[80];
182         int i;
183
184         for (i = 0; i < nbcinfo; i++) {
185                 sprintf(line, "    cd%d: Device 0x%x\n", i,
186                     bcinfo[i].bc_sp.sp_devicespec);
187                 pager_output(line);
188         }
189 }
190
191 /*
192  * Attempt to open the disk described by (dev) for use by (f).
193  */
194 static int 
195 bc_open(struct open_file *f, ...)
196 {
197         va_list ap;
198         struct i386_devdesc *dev;
199
200         va_start(ap, f);
201         dev = va_arg(ap, struct i386_devdesc *);
202         va_end(ap);
203         if (dev->d_unit >= nbcinfo) {
204                 DEBUG("attempt to open nonexistent disk");
205                 return(ENXIO);
206         }
207
208         return(0);
209 }
210  
211 static int 
212 bc_close(struct open_file *f)
213 {
214
215         return(0);
216 }
217
218 static int 
219 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
220     size_t *rsize)
221 {
222         struct i386_devdesc *dev;
223         int unit;
224         int blks;
225 #ifdef BD_SUPPORT_FRAGS
226         char fragbuf[BIOSCD_SECSIZE];
227         size_t fragsize;
228
229         fragsize = size % BIOSCD_SECSIZE;
230 #else
231         if (size % BIOSCD_SECSIZE)
232                 return (EINVAL);
233 #endif
234
235         if (rw != F_READ)
236                 return(EROFS);
237         dev = (struct i386_devdesc *)devdata;
238         unit = dev->d_unit;
239         blks = size / BIOSCD_SECSIZE;
240         if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
241                 return (EINVAL);
242         dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
243         DEBUG("read %d from %lld to %p", blks, dblk, buf);
244
245         if (rsize)
246                 *rsize = 0;
247         if (blks && bc_read(unit, dblk, blks, buf)) {
248                 DEBUG("read error");
249                 return (EIO);
250         }
251 #ifdef BD_SUPPORT_FRAGS
252         DEBUG("frag read %d from %lld+%d to %p", 
253             fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
254         if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
255                 DEBUG("frag read error");
256                 return(EIO);
257         }
258         bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
259 #endif  
260         if (rsize)
261                 *rsize = size;
262         return (0);
263 }
264
265 /* Max number of sectors to bounce-buffer at a time. */
266 #define CD_BOUNCEBUF    8
267
268 static int
269 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
270 {
271         u_int maxfer, resid, result, retry, x;
272         caddr_t bbuf, p, xp;
273         static struct edd_packet packet;
274         int biosdev;
275 #ifdef DISK_DEBUG
276         int error;
277 #endif
278     
279         /* Just in case some idiot actually tries to read -1 blocks... */
280         if (blks < 0)
281                 return (-1);
282
283         /* If nothing to do, just return succcess. */
284         if (blks == 0)
285                 return (0);
286
287         /* Decide whether we have to bounce */
288         if (VTOP(dest) >> 20 != 0) {
289                 /* 
290                  * The destination buffer is above first 1MB of
291                  * physical memory so we have to arrange a suitable
292                  * bounce buffer.
293                  */
294                 x = min(CD_BOUNCEBUF, (unsigned)blks);
295                 bbuf = alloca(x * BIOSCD_SECSIZE);
296                 maxfer = x;
297         } else {
298                 bbuf = NULL;
299                 maxfer = 0;
300         }
301         
302         biosdev = bc_unit2bios(unit);
303         resid = blks;
304         p = dest;
305
306         while (resid > 0) {
307                 if (bbuf)
308                         xp = bbuf;
309                 else
310                         xp = p;
311                 x = resid;
312                 if (maxfer > 0)
313                         x = min(x, maxfer);
314
315                 /*
316                  * Loop retrying the operation a couple of times.  The BIOS
317                  * may also retry.
318                  */
319                 for (retry = 0; retry < 3; retry++) {
320                         /* If retrying, reset the drive */
321                         if (retry > 0) {
322                                 v86.ctl = V86_FLAGS;
323                                 v86.addr = 0x13;
324                                 v86.eax = 0;
325                                 v86.edx = biosdev;
326                                 v86int();
327                         }
328
329                         packet.len = sizeof(struct edd_packet);
330                         packet.count = x;
331                         packet.off = VTOPOFF(xp);
332                         packet.seg = VTOPSEG(xp);
333                         packet.lba = dblk;
334                         v86.ctl = V86_FLAGS;
335                         v86.addr = 0x13;
336                         v86.eax = 0x4200;
337                         v86.edx = biosdev;
338                         v86.ds = VTOPSEG(&packet);
339                         v86.esi = VTOPOFF(&packet);
340                         v86int();
341                         result = (v86.efl & PSL_C);
342                         if (result == 0)
343                                 break;
344                 }
345         
346 #ifdef DISK_DEBUG
347                 error = (v86.eax >> 8) & 0xff;
348 #endif
349                 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
350                     VTOP(p), result ? "failed" : "ok");
351                 DEBUG("unit %d  status 0x%x", unit, error);
352                 if (bbuf != NULL)
353                         bcopy(bbuf, p, x * BIOSCD_SECSIZE);
354                 p += (x * BIOSCD_SECSIZE);
355                 dblk += x;
356                 resid -= x;
357         }
358         
359 /*      hexdump(dest, (blks * BIOSCD_SECSIZE)); */
360         return(0);
361 }
362
363 /*
364  * Return a suitable dev_t value for (dev).
365  */
366 int
367 bc_getdev(struct i386_devdesc *dev)
368 {
369     int biosdev, unit;
370     int major;
371     int rootdev;
372
373     unit = dev->d_unit;
374     biosdev = bc_unit2bios(unit);
375     DEBUG("unit %d BIOS device %d", unit, biosdev);
376     if (biosdev == -1)                          /* not a BIOS device */
377         return(-1);
378
379     /*
380      * XXX: Need to examine device spec here to figure out if SCSI or
381      * ATAPI.  No idea on how to figure out device number.  All we can
382      * really pass to the kernel is what bus and device on which bus we
383      * were booted from, which dev_t isn't well suited to since those
384      * number don't match to unit numbers very well.  We may just need
385      * to engage in a hack where we pass -C to the boot args if we are
386      * the boot device.
387      */
388     major = ACDMAJOR;
389     unit = 0;   /* XXX */
390
391     /* XXX: Assume partition 'a'. */
392     rootdev = MAKEBOOTDEV(major, 0, unit, 0);
393     DEBUG("dev is 0x%x\n", rootdev);
394     return(rootdev);
395 }