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