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>
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.
34 * Ideas and algorithms from:
36 * - FreeBSD libi386/biosdisk.c
42 #include <sys/param.h>
43 #include <machine/bootinfo.h>
47 #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
;
164 v86
.ds
= VTOPSEG(&bcinfo
[nbcinfo
].bc_sp
);
165 v86
.esi
= VTOPOFF(&bcinfo
[nbcinfo
].bc_sp
);
167 if ((v86
.eax
& 0xff00) != 0)
170 printf("BIOS CD is cd%d\n", nbcinfo
);
172 bcache_add_dev(nbcinfo
); /* register cd device in bcache */
177 * Print information about disks
180 bc_print(int verbose
)
188 printf("%s devices:", bioscd
.dv_name
);
189 if ((ret
= pager_output("\n")) != 0)
192 for (i
= 0; i
< nbcinfo
; i
++) {
193 sprintf(line
, " cd%d: Device 0x%x\n", i
,
194 bcinfo
[i
].bc_sp
.sp_devicespec
);
195 ret
= pager_output(line
);
203 * Attempt to open the disk described by (dev) for use by (f).
206 bc_open(struct open_file
*f
, ...)
209 struct i386_devdesc
*dev
;
212 dev
= va_arg(ap
, struct i386_devdesc
*);
214 if (dev
->d_unit
>= nbcinfo
) {
215 DEBUG("attempt to open nonexistent disk");
220 if (BC(dev
).bc_bcache
== NULL
)
221 BC(dev
).bc_bcache
= bcache_allocate();
226 bc_close(struct open_file
*f
)
228 struct i386_devdesc
*dev
;
230 dev
= (struct i386_devdesc
*)f
->f_devdata
;
232 if (BC(dev
).bc_open
== 0) {
233 bcache_free(BC(dev
).bc_bcache
);
234 BC(dev
).bc_bcache
= NULL
;
240 bc_strategy(void *devdata
, int rw
, daddr_t dblk
, size_t size
,
241 char *buf
, size_t *rsize
)
243 struct bcache_devdata bcd
;
244 struct i386_devdesc
*dev
;
246 dev
= (struct i386_devdesc
*)devdata
;
247 bcd
.dv_strategy
= bc_realstrategy
;
248 bcd
.dv_devdata
= devdata
;
249 bcd
.dv_cache
= BC(dev
).bc_bcache
;
251 return (bcache_strategy(&bcd
, rw
, dblk
, size
, buf
, rsize
));
255 bc_realstrategy(void *devdata
, int rw
, daddr_t dblk
, size_t size
,
256 char *buf
, size_t *rsize
)
258 struct i386_devdesc
*dev
;
261 #ifdef BD_SUPPORT_FRAGS
262 char fragbuf
[BIOSCD_SECSIZE
];
265 fragsize
= size
% BIOSCD_SECSIZE
;
267 if (size
% BIOSCD_SECSIZE
)
271 if ((rw
& F_MASK
) != F_READ
)
273 dev
= (struct i386_devdesc
*)devdata
;
275 blks
= size
/ BIOSCD_SECSIZE
;
276 if (dblk
% (BIOSCD_SECSIZE
/ DEV_BSIZE
) != 0)
278 dblk
/= (BIOSCD_SECSIZE
/ DEV_BSIZE
);
279 DEBUG("read %d from %lld to %p", blks
, dblk
, buf
);
283 if ((blks
= bc_read(unit
, dblk
, blks
, buf
)) < 0) {
287 if (size
/ BIOSCD_SECSIZE
> blks
) {
289 *rsize
= blks
* BIOSCD_SECSIZE
;
293 #ifdef BD_SUPPORT_FRAGS
294 DEBUG("frag read %d from %lld+%d to %p",
295 fragsize
, dblk
, blks
, buf
+ (blks
* BIOSCD_SECSIZE
));
296 if (fragsize
&& bc_read(unit
, dblk
+ blks
, 1, fragbuf
) != 1) {
299 *rsize
= blks
* BIOSCD_SECSIZE
;
302 DEBUG("frag read error");
305 bcopy(fragbuf
, buf
+ (blks
* BIOSCD_SECSIZE
), fragsize
);
312 /* return negative value for an error, otherwise blocks read */
314 bc_read(int unit
, daddr_t dblk
, int blks
, caddr_t dest
)
316 u_int maxfer
, resid
, result
, retry
, x
;
318 static struct edd_packet packet
;
324 /* Just in case some idiot actually tries to read -1 blocks... */
328 /* If nothing to do, just return succcess. */
332 /* Decide whether we have to bounce */
333 if (VTOP(dest
) >> 20 != 0) {
335 * The destination buffer is above first 1MB of
336 * physical memory so we have to arrange a suitable
339 x
= V86_IO_BUFFER_SIZE
/ BIOSCD_SECSIZE
;
341 panic("BUG: Real mode buffer is too small\n");
342 x
= min(x
, (unsigned)blks
);
343 bbuf
= PTOV(V86_IO_BUFFER
);
350 biosdev
= bc_unit2bios(unit
);
364 * Loop retrying the operation a couple of times. The BIOS
367 for (retry
= 0; retry
< 3; retry
++) {
368 /* If retrying, reset the drive */
377 packet
.len
= sizeof(struct edd_packet
);
379 packet
.off
= VTOPOFF(xp
);
380 packet
.seg
= VTOPSEG(xp
);
386 v86
.ds
= VTOPSEG(&packet
);
387 v86
.esi
= VTOPOFF(&packet
);
389 result
= V86_CY(v86
.efl
);
392 /* fall back to 1 sector read */
397 error
= (v86
.eax
>> 8) & 0xff;
399 DEBUG("%d sectors from %lld to %p (0x%x) %s", x
, dblk
, p
,
400 VTOP(p
), result
? "failed" : "ok");
401 DEBUG("unit %d status 0x%x", unit
, error
);
403 /* still an error? break off */
408 bcopy(bbuf
, p
, x
* BIOSCD_SECSIZE
);
409 p
+= (x
* BIOSCD_SECSIZE
);
414 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
416 if (blks
- resid
== 0)
417 return (-1); /* read failed */
419 return (blks
- resid
);
423 * Return a suitable dev_t value for (dev).
426 bc_getdev(struct i386_devdesc
*dev
)
433 biosdev
= bc_unit2bios(unit
);
434 DEBUG("unit %d BIOS device %d", unit
, biosdev
);
435 if (biosdev
== -1) /* not a BIOS device */
439 * XXX: Need to examine device spec here to figure out if SCSI or
440 * ATAPI. No idea on how to figure out device number. All we can
441 * really pass to the kernel is what bus and device on which bus we
442 * were booted from, which dev_t isn't well suited to since those
443 * number don't match to unit numbers very well. We may just need
444 * to engage in a hack where we pass -C to the boot args if we are
450 /* XXX: Assume partition 'a'. */
451 rootdev
= MAKEBOOTDEV(major
, 0, unit
, 0);
452 DEBUG("dev is 0x%x\n", rootdev
);