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
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 $
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/reboot.h>
44 #include <machine/param.h>
45 #include <machine/psl.h>
49 #include <bootstrap.h>
53 #define BIOSCD_SECSIZE 2048
54 #define BUFSIZE (1 * BIOSCD_SECSIZE)
57 /* Major numbers for devices we frontend for. */
62 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
64 # define DEBUG(fmt, args...)
67 struct specification_packet {
73 u_short sp_devicespec;
74 u_short sp_buffersegment;
75 u_short sp_loadsegment;
76 u_short sp_sectorcount;
82 * List of BIOS devices, translation from disk unit number to
85 static struct bcinfo {
86 int bc_unit; /* BIOS unit number */
87 struct specification_packet bc_sp;
89 static int nbcinfo = 0;
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);
101 struct devsw bioscd = {
114 * Translate between BIOS device numbers and our private unit numbers.
117 bc_bios2unit(int biosdev)
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)
131 bc_unit2bios(int unit)
133 if ((unit >= 0) && (unit < nbcinfo))
134 return(bcinfo[unit].bc_unit);
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.
154 if (nbcinfo >= MAXBCDEV)
156 bcinfo[nbcinfo].bc_unit = biosdev;
161 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
162 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
164 if ((v86.eax & 0xff00) != 0)
167 printf("BIOS CD is cd%d\n", nbcinfo);
173 * Print information about disks
176 bc_print(int verbose)
181 for (i = 0; i < nbcinfo; i++) {
182 sprintf(line, " cd%d: Device 0x%x\n", i,
183 bcinfo[i].bc_sp.sp_devicespec);
189 * Attempt to open the disk described by (dev) for use by (f).
192 bc_open(struct open_file *f, ...)
195 struct i386_devdesc *dev;
199 dev = __va_arg(ap, struct i386_devdesc *);
201 if (dev->d_kind.bioscd.unit >= nbcinfo) {
202 DEBUG("attempt to open nonexistent disk");
210 bc_close(struct open_file *f)
217 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
220 struct i386_devdesc *dev;
223 #ifdef BD_SUPPORT_FRAGS
224 char fragbuf[BIOSCD_SECSIZE];
227 fragsize = size % BIOSCD_SECSIZE;
229 if (size % BIOSCD_SECSIZE)
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)
240 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
241 DEBUG("read %d from %d to %p", blks, dblk, buf);
245 if (blks && bc_read(unit, dblk, blks, buf)) {
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");
256 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
264 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
266 u_int result, resid, retry;
267 static unsigned short packet[8];
273 /* Just in case some idiot actually tries to read -1 blocks... */
277 /* If nothing to do, just return succcess. */
281 biosdev = bc_unit2bios(unit);
283 * Loop retrying the operation a couple of times. The BIOS
286 for (retry = 0; retry < 3; retry++) {
287 /* If retrying, reset the drive */
298 packet[2] = VTOPOFF(dest);
299 packet[3] = VTOPSEG(dest);
300 packet[4] = dblk & 0xffff;
301 packet[5] = dblk >> 16;
308 v86.ds = VTOPSEG(packet);
309 v86.esi = VTOPOFF(packet);
311 result = (v86.efl & PSL_C);
317 error = (v86.eax >> 8) & 0xff;
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);
323 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
328 * Return a suitable dev_t value for (dev).
331 bc_getdev(struct i386_devdesc *dev)
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 */
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
355 /* XXX: Assume partition 'a'. */
356 rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
357 DEBUG("dev is 0x%x\n", rootdev);