Fully synchronize sys/boot from FreeBSD-5.x, but add / to the module path
[dragonfly.git] / sys / boot / pc32 / 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  * $FreeBSD: src/sys/boot/i386/libi386/bioscd.c,v 1.5 2003/08/25 23:28:31 obrien Exp $
28  * $DragonFly: src/sys/boot/pc32/libi386/bioscd.c,v 1.4 2003/11/10 06:08:36 dillon Exp $
29  */
30 /*
31  * BIOS CD device handling for CD's that have been booted off of via no
32  * emulation booting as defined in the El Torito standard.
33  * 
34  * Ideas and algorithms from:
35  *
36  * - FreeBSD libi386/biosdisk.c
37  *
38  */
39
40 #include <stand.h>
41
42 #include <sys/param.h>
43 #include <machine/bootinfo.h>
44 #include <machine/psl.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_realstrategy(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         for (i = 0; i < nbcinfo; i++) {
122                 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
123                 if (bcinfo[i].bc_unit == biosdev)
124                         return(i);
125         }
126         return(-1);
127 }
128
129 int
130 bc_unit2bios(int unit)
131 {
132         if ((unit >= 0) && (unit < nbcinfo))
133                 return(bcinfo[unit].bc_unit);
134         return(-1);
135 }
136
137 /*    
138  * We can't quiz, we have to be told what device to use, so this functoin
139  * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
140  * device number to add.
141  */
142 static int
143 bc_init(void) 
144 {
145
146         return (0);
147 }
148
149 int
150 bc_add(int biosdev)
151 {
152
153         if (nbcinfo >= MAXBCDEV)
154                 return (-1);
155         bcinfo[nbcinfo].bc_unit = biosdev;
156         v86.ctl = V86_FLAGS;
157         v86.addr = 0x13;
158         v86.eax = 0x4b01;
159         v86.edx = biosdev;
160         v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
161         v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
162         v86int();
163         if ((v86.eax & 0xff00) != 0)
164                 return (-1);
165
166         printf("BIOS CD is cd%d\n", nbcinfo);
167         nbcinfo++;
168         return(0);
169 }
170
171 /*
172  * Print information about disks
173  */
174 static void
175 bc_print(int verbose)
176 {
177         int i;
178         char line[80];
179     
180         for (i = 0; i < nbcinfo; i++) {
181                 sprintf(line, "    cd%d: Device 0x%x\n", i,
182                     bcinfo[i].bc_sp.sp_devicespec);
183                 pager_output(line);
184         }
185 }
186
187 /*
188  * Attempt to open the disk described by (dev) for use by (f).
189  */
190 static int 
191 bc_open(struct open_file *f, ...)
192 {
193         va_list ap;
194         struct i386_devdesc *dev;
195         int error;
196
197         va_start(ap, f);
198         dev = va_arg(ap, struct i386_devdesc *);
199         va_end(ap);
200         if (dev->d_kind.bioscd.unit >= nbcinfo) {
201                 DEBUG("attempt to open nonexistent disk");
202                 return(ENXIO);
203         }
204
205         return(0);
206 }
207  
208 static int 
209 bc_close(struct open_file *f)
210 {
211
212         return(0);
213 }
214
215 static int 
216 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
217     size_t *rsize)
218 {
219         struct i386_devdesc *dev;
220         int unit;
221         int blks;
222 #ifdef BD_SUPPORT_FRAGS
223         char fragbuf[BIOSCD_SECSIZE];
224         size_t fragsize;
225
226         fragsize = size % BIOSCD_SECSIZE;
227 #else
228         if (size % BIOSCD_SECSIZE)
229                 return (EINVAL);
230 #endif
231
232         if (rw != F_READ)
233                 return(EROFS);
234         dev = (struct i386_devdesc *)devdata;
235         unit = dev->d_kind.bioscd.unit;
236         blks = size / BIOSCD_SECSIZE;
237         if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
238                 return (EINVAL);
239         dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
240         DEBUG("read %d from %d to %p", blks, dblk, buf);
241
242         if (rsize)
243                 *rsize = 0;
244         if (blks && bc_read(unit, dblk, blks, buf)) {
245                 DEBUG("read error");
246                 return (EIO);
247         }
248 #ifdef BD_SUPPORT_FRAGS
249         DEBUG("bc_strategy: frag read %d from %d+%d to %p", 
250             fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
251         if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
252                 DEBUG("frag read error");
253                 return(EIO);
254         }
255         bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
256 #endif  
257         if (rsize)
258                 *rsize = size;
259         return (0);
260 }
261
262 static int
263 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
264 {
265         u_int result, resid, retry;
266         static unsigned short packet[8];
267         int biosdev;
268 #ifdef DISK_DEBUG
269         int error;
270 #endif
271     
272         /* Just in case some idiot actually tries to read -1 blocks... */
273         if (blks < 0)
274                 return (-1);
275
276         /* If nothing to do, just return succcess. */
277         if (blks == 0)
278                 return (0);
279
280         biosdev = bc_unit2bios(unit);
281         /*
282          * Loop retrying the operation a couple of times.  The BIOS
283          * may also retry.
284          */
285         for (retry = 0; retry < 3; retry++) {
286                 /* If retrying, reset the drive */
287                 if (retry > 0) {
288                         v86.ctl = V86_FLAGS;
289                         v86.addr = 0x13;
290                         v86.eax = 0;
291                         v86.edx = biosdev;
292                         v86int();
293                 }
294             
295                 packet[0] = 0x10;
296                 packet[1] = blks;
297                 packet[2] = VTOPOFF(dest);
298                 packet[3] = VTOPSEG(dest);
299                 packet[4] = dblk & 0xffff;
300                 packet[5] = dblk >> 16;
301                 packet[6] = 0;
302                 packet[7] = 0;
303                 v86.ctl = V86_FLAGS;
304                 v86.addr = 0x13;
305                 v86.eax = 0x4200;
306                 v86.edx = biosdev;
307                 v86.ds = VTOPSEG(packet);
308                 v86.esi = VTOPOFF(packet);
309                 v86int();
310                 result = (v86.efl & PSL_C);
311                 if (result == 0)
312                         break;
313         }
314         
315 #ifdef DISK_DEBUG
316         error = (v86.eax >> 8) & 0xff;
317 #endif
318         DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
319             VTOP(dest), result ? "failed" : "ok");
320         DEBUG("unit %d  status 0x%x",  unit, error);
321         
322 /*      hexdump(dest, (blks * BIOSCD_SECSIZE)); */
323         return(0);
324 }
325
326 /*
327  * Return a suitable dev_t value for (dev).
328  */
329 int
330 bc_getdev(struct i386_devdesc *dev)
331 {
332     int biosdev, unit;
333     int major;
334     int rootdev;
335
336     unit = dev->d_kind.bioscd.unit;
337     biosdev = bc_unit2bios(unit);
338     DEBUG("unit %d BIOS device %d", unit, biosdev);
339     if (biosdev == -1)                          /* not a BIOS device */
340         return(-1);
341
342     /*
343      * XXX: Need to examine device spec here to figure out if SCSI or
344      * ATAPI.  No idea on how to figure out device number.  All we can
345      * really pass to the kernel is what bus and device on which bus we
346      * were booted from, which dev_t isn't well suited to since those
347      * number don't match to unit numbers very well.  We may just need
348      * to engage in a hack where we pass -C to the boot args if we are
349      * the boot device.
350      */
351     major = ACDMAJOR;
352     unit = 0;   /* XXX */
353
354     /* XXX: Assume partition 'a'. */
355     rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
356     DEBUG("dev is 0x%x\n", rootdev);
357     return(rootdev);
358 }