2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
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.
35 * Ideas and algorithms from:
37 * - FreeBSD libi386/biosdisk.c
43 #include <sys/param.h>
44 #include <machine/bootinfo.h>
48 #include <bootstrap.h>
52 #define BIOSCD_SECSIZE 2048
53 #define BUFSIZE (1 * BIOSCD_SECSIZE)
56 /* Major numbers for devices we frontend for. */
61 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
63 # define DEBUG(fmt, args...)
66 struct specification_packet {
72 u_short sp_devicespec;
73 u_short sp_buffersegment;
74 u_short sp_loadsegment;
75 u_short sp_sectorcount;
81 * List of BIOS devices, translation from disk unit number to
84 static struct bcinfo {
85 int bc_unit; /* BIOS unit number */
86 struct specification_packet bc_sp;
87 int bc_open; /* reference counter */
88 void *bc_bcache; /* buffer cache data */
90 static int nbcinfo = 0;
92 #define BC(dev) (bcinfo[(dev)->d_unit])
94 static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
95 static int bc_init(void);
96 static int bc_strategy(void *devdata, int flag, daddr_t dblk,
97 size_t size, char *buf, size_t *rsize);
98 static int bc_realstrategy(void *devdata, int flag, daddr_t dblk,
99 size_t size, char *buf, size_t *rsize);
100 static int bc_open(struct open_file *f, ...);
101 static int bc_close(struct open_file *f);
102 static int bc_print(int verbose);
104 struct devsw bioscd = {
117 * Translate between BIOS device numbers and our private unit numbers.
120 bc_bios2unit(int biosdev)
124 DEBUG("looking for bios device 0x%x", biosdev);
125 for (i = 0; i < nbcinfo; i++) {
126 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
127 if (bcinfo[i].bc_unit == biosdev)
134 bc_unit2bios(int unit)
136 if ((unit >= 0) && (unit < nbcinfo))
137 return(bcinfo[unit].bc_unit);
142 * We can't quiz, we have to be told what device to use, so this functoin
143 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS
144 * device number to add.
157 if (nbcinfo >= MAXBCDEV)
159 bcinfo[nbcinfo].bc_unit = biosdev;
161 /* SCSI CD-ROM only */
162 if ((biosdev & 0xf0) != 0xa0)
164 if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
167 printf("BIOS CD is cd%d\n", nbcinfo);
169 bcache_add_dev(nbcinfo); /* register cd device in bcache */
174 * Print information about disks
177 bc_print(int verbose)
185 printf("%s devices:", bioscd.dv_name);
186 if ((ret = pager_output("\n")) != 0)
189 for (i = 0; i < nbcinfo; i++) {
190 sprintf(line, " cd%d: Device 0x%x\n", i,
191 bcinfo[i].bc_sp.sp_devicespec);
192 if ((ret = pager_output(line)) != 0)
199 * Attempt to open the disk described by (dev) for use by (f).
202 bc_open(struct open_file *f, ...)
205 struct i386_devdesc *dev;
208 dev = va_arg(ap, struct i386_devdesc *);
210 if (dev->d_unit >= nbcinfo) {
211 DEBUG("attempt to open nonexistent disk");
216 if (BC(dev).bc_bcache == NULL)
217 BC(dev).bc_bcache = bcache_allocate();
222 bc_close(struct open_file *f)
224 struct i386_devdesc *dev;
226 dev = (struct i386_devdesc *)f->f_devdata;
228 if (BC(dev).bc_open == 0) {
229 bcache_free(BC(dev).bc_bcache);
230 BC(dev).bc_bcache = NULL;
236 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
237 char *buf, size_t *rsize)
239 struct bcache_devdata bcd;
240 struct i386_devdesc *dev;
242 dev = (struct i386_devdesc *)devdata;
243 bcd.dv_strategy = bc_realstrategy;
244 bcd.dv_devdata = devdata;
245 bcd.dv_cache = BC(dev).bc_bcache;
247 return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
251 bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
252 char *buf, size_t *rsize)
254 struct i386_devdesc *dev;
257 #ifdef BD_SUPPORT_FRAGS
258 char fragbuf[BIOSCD_SECSIZE];
261 fragsize = size % BIOSCD_SECSIZE;
263 if (size % BIOSCD_SECSIZE)
269 dev = (struct i386_devdesc *)devdata;
271 blks = size / BIOSCD_SECSIZE;
272 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
274 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
275 DEBUG("read %d from %lld to %p", blks, dblk, buf);
279 if (blks && bc_read(unit, dblk, blks, buf)) {
283 #ifdef BD_SUPPORT_FRAGS
284 DEBUG("frag read %d from %lld+%d to %p",
285 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
286 if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
287 DEBUG("frag read error");
290 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
297 /* Max number of sectors to bounce-buffer at a time. */
298 #define CD_BOUNCEBUF 8
301 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
303 u_int maxfer, resid, result, retry, x;
310 /* Just in case some idiot actually tries to read -1 blocks... */
314 /* If nothing to do, just return succcess. */
318 /* Decide whether we have to bounce */
319 if (VTOP(dest) >> 20 != 0) {
321 * The destination buffer is above first 1MB of
322 * physical memory so we have to arrange a suitable
325 x = min(CD_BOUNCEBUF, (unsigned)blks);
326 bbuf = alloca(x * BIOSCD_SECSIZE);
333 biosdev = bc_unit2bios(unit);
347 * Loop retrying the operation a couple of times. The BIOS
350 for (retry = 0; retry < 3; retry++) {
351 /* If retrying, reset the drive */
355 v86.eax = 0x0300 | biosdev;
361 v86.eax = 0x0600 | (biosdev & 0x7f);
362 v86.ebx = x * BIOSCD_SECSIZE;
363 v86.ecx = dblk & 0xffff;
364 v86.edx = (dblk >> 16) & 0xffff;
365 v86.ebp = VTOPOFF(xp);
366 v86.es = VTOPSEG(xp);
368 result = V86_CY(v86.efl);
374 error = (v86.eax >> 8) & 0xff;
376 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
377 VTOP(p), result ? "failed" : "ok");
378 DEBUG("unit %d status 0x%x", unit, error);
380 bcopy(bbuf, p, x * BIOSCD_SECSIZE);
381 p += (x * BIOSCD_SECSIZE);
386 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
391 * Return a suitable dev_t value for (dev).
394 bc_getdev(struct i386_devdesc *dev)
396 int biosdev, unit, device;
401 biosdev = bc_unit2bios(unit);
402 DEBUG("unit %d BIOS device %d", unit, biosdev);
403 if (biosdev == -1) /* not a BIOS device */
406 device = biosdev & 0xf0;
409 else if (device == 0xa0)
416 /* XXX: Assume partition 'a'. */
417 rootdev = MAKEBOOTDEV(major, 0, unit, 0);
418 DEBUG("dev is 0x%x\n", rootdev);