Core integer types header file reorganization stage 1/2: Create and/or modify
[dragonfly.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  * $FreeBSD: src/sys/boot/i386/libi386/bioscd.c,v 1.2.2.1 2001/12/21 22:19:58 jhb Exp $
28  * $DragonFly: src/sys/boot/i386/libi386/Attic/bioscd.c,v 1.3 2003/11/09 02:22:33 dillon Exp $
29  */
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/reboot.h>
44 #include <machine/param.h>
45 #include <machine/psl.h>
46
47 #include <stdarg.h>
48
49 #include <bootstrap.h>
50 #include <btxv86.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 } bcinfo [MAXBCDEV];
89 static int nbcinfo = 0;
90
91 static int      bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
92 static int      bc_init(void);
93 static int      bc_strategy(void *devdata, int flag, daddr_t dblk,
94                     size_t size, char *buf, size_t *rsize);
95 static int      bc_realstrategy(void *devdata, int flag, daddr_t dblk,
96                     size_t size, char *buf, size_t *rsize);
97 static int      bc_open(struct open_file *f, ...);
98 static int      bc_close(struct open_file *f);
99 static void     bc_print(int verbose);
100
101 struct devsw bioscd = {
102         "cd", 
103         DEVT_CD, 
104         bc_init,
105         bc_strategy, 
106         bc_open, 
107         bc_close, 
108         noioctl,
109         bc_print,
110         NULL
111 };
112
113 /*
114  * Translate between BIOS device numbers and our private unit numbers.
115  */
116 int
117 bc_bios2unit(int biosdev)
118 {
119         int i;
120     
121         DEBUG("looking for bios device 0x%x", biosdev);
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
154         if (nbcinfo >= MAXBCDEV)
155                 return (-1);
156         bcinfo[nbcinfo].bc_unit = biosdev;
157         v86.ctl = V86_FLAGS;
158         v86.addr = 0x13;
159         v86.eax = 0x4b01;
160         v86.edx = biosdev;
161         v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
162         v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
163         v86int();
164         if ((v86.eax & 0xff00) != 0)
165                 return (-1);
166
167         printf("BIOS CD is cd%d\n", nbcinfo);
168         nbcinfo++;
169         return(0);
170 }
171
172 /*
173  * Print information about disks
174  */
175 static void
176 bc_print(int verbose)
177 {
178         int i;
179         char line[80];
180     
181         for (i = 0; i < nbcinfo; i++) {
182                 sprintf(line, "    cd%d: Device 0x%x\n", i,
183                     bcinfo[i].bc_sp.sp_devicespec);
184                 pager_output(line);
185         }
186 }
187
188 /*
189  * Attempt to open the disk described by (dev) for use by (f).
190  */
191 static int 
192 bc_open(struct open_file *f, ...)
193 {
194         __va_list ap;
195         struct i386_devdesc *dev;
196         int error;
197
198         __va_start(ap, f);
199         dev = __va_arg(ap, struct i386_devdesc *);
200         __va_end(ap);
201         if (dev->d_kind.bioscd.unit >= nbcinfo) {
202                 DEBUG("attempt to open nonexistent disk");
203                 return(ENXIO);
204         }
205
206         return(0);
207 }
208  
209 static int 
210 bc_close(struct open_file *f)
211 {
212
213         return(0);
214 }
215
216 static int 
217 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
218     size_t *rsize)
219 {
220         struct i386_devdesc *dev;
221         int unit;
222         int blks;
223 #ifdef BD_SUPPORT_FRAGS
224         char fragbuf[BIOSCD_SECSIZE];
225         size_t fragsize;
226
227         fragsize = size % BIOSCD_SECSIZE;
228 #else
229         if (size % BIOSCD_SECSIZE)
230                 return (EINVAL);
231 #endif
232
233         if (rw != F_READ)
234                 return(EROFS);
235         dev = (struct i386_devdesc *)devdata;
236         unit = dev->d_kind.bioscd.unit;
237         blks = size / BIOSCD_SECSIZE;
238         if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
239                 return (EINVAL);
240         dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
241         DEBUG("read %d from %d to %p", blks, dblk, buf);
242
243         if (rsize)
244                 *rsize = 0;
245         if (blks && bc_read(unit, dblk, blks, buf)) {
246                 DEBUG("read error");
247                 return (EIO);
248         }
249 #ifdef BD_SUPPORT_FRAGS
250         DEBUG("bc_strategy: frag read %d from %d+%d to %p", 
251             fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
252         if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
253                 DEBUG("frag read error");
254                 return(EIO);
255         }
256         bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
257 #endif  
258         if (rsize)
259                 *rsize = size;
260         return (0);
261 }
262
263 static int
264 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
265 {
266         u_int result, resid, retry;
267         static unsigned short packet[8];
268         int biosdev;
269 #ifdef DISK_DEBUG
270         int error;
271 #endif
272     
273         /* Just in case some idiot actually tries to read -1 blocks... */
274         if (blks < 0)
275                 return (-1);
276
277         /* If nothing to do, just return succcess. */
278         if (blks == 0)
279                 return (0);
280
281         biosdev = bc_unit2bios(unit);
282         /*
283          * Loop retrying the operation a couple of times.  The BIOS
284          * may also retry.
285          */
286         for (retry = 0; retry < 3; retry++) {
287                 /* If retrying, reset the drive */
288                 if (retry > 0) {
289                         v86.ctl = V86_FLAGS;
290                         v86.addr = 0x13;
291                         v86.eax = 0;
292                         v86.edx = biosdev;
293                         v86int();
294                 }
295             
296                 packet[0] = 0x10;
297                 packet[1] = blks;
298                 packet[2] = VTOPOFF(dest);
299                 packet[3] = VTOPSEG(dest);
300                 packet[4] = dblk & 0xffff;
301                 packet[5] = dblk >> 16;
302                 packet[6] = 0;
303                 packet[7] = 0;
304                 v86.ctl = V86_FLAGS;
305                 v86.addr = 0x13;
306                 v86.eax = 0x4200;
307                 v86.edx = biosdev;
308                 v86.ds = VTOPSEG(packet);
309                 v86.esi = VTOPOFF(packet);
310                 v86int();
311                 result = (v86.efl & PSL_C);
312                 if (result == 0)
313                         break;
314         }
315         
316 #ifdef DISK_DEBUG
317         error = (v86.eax >> 8) & 0xff;
318 #endif
319         DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
320             VTOP(dest), result ? "failed" : "ok");
321         DEBUG("unit %d  status 0x%x",  unit, error);
322         
323 /*      hexdump(dest, (blks * BIOSCD_SECSIZE)); */
324         return(0);
325 }
326
327 /*
328  * Return a suitable dev_t value for (dev).
329  */
330 int
331 bc_getdev(struct i386_devdesc *dev)
332 {
333     int biosdev, unit;
334     int major;
335     int rootdev;
336
337     unit = dev->d_kind.bioscd.unit;
338     biosdev = bc_unit2bios(unit);
339     DEBUG("unit %d BIOS device %d", unit, biosdev);
340     if (biosdev == -1)                          /* not a BIOS device */
341         return(-1);
342
343     /*
344      * XXX: Need to examine device spec here to figure out if SCSI or
345      * ATAPI.  No idea on how to figure out device number.  All we can
346      * really pass to the kernel is what bus and device on which bus we
347      * were booted from, which dev_t isn't well suited to since those
348      * number don't match to unit numbers very well.  We may just need
349      * to engage in a hack where we pass -C to the boot args if we are
350      * the boot device.
351      */
352     major = ACDMAJOR;
353     unit = 0;   /* XXX */
354
355     /* XXX: Assume partition 'a'. */
356     rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
357     DEBUG("dev is 0x%x\n", rootdev);
358     return(rootdev);
359 }