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