8010 loader: want mechanism to avoid RA with bcache
[unleashed.git] / usr / src / boot / sys / boot / i386 / libi386 / bioscd.c
blobac76c08c697fcde39a709323d147087f89f0019c
1 /*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
4 * All rights reserved.
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.
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.
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
40 #include <stand.h>
42 #include <sys/param.h>
43 #include <machine/bootinfo.h>
45 #include <stdarg.h>
47 #include <bootstrap.h>
48 #include <btxv86.h>
49 #include <edd.h>
50 #include "libi386.h"
52 #define BIOSCD_SECSIZE 2048
53 #define BUFSIZE (1 * BIOSCD_SECSIZE)
54 #define MAXBCDEV 1
56 /* Major numbers for devices we frontend for. */
57 #define ACDMAJOR 117
58 #define CDMAJOR 15
60 #ifdef DISK_DEBUG
61 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
62 #else
63 # define DEBUG(fmt, args...)
64 #endif
66 struct specification_packet {
67 u_char sp_size;
68 u_char sp_bootmedia;
69 u_char sp_drive;
70 u_char sp_controller;
71 u_int sp_lba;
72 u_short sp_devicespec;
73 u_short sp_buffersegment;
74 u_short sp_loadsegment;
75 u_short sp_sectorcount;
76 u_short sp_cylsec;
77 u_char sp_head;
81 * List of BIOS devices, translation from disk unit number to
82 * BIOS unit number.
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 */
89 } bcinfo [MAXBCDEV];
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 = {
105 "cd",
106 DEVT_CD,
107 bc_init,
108 bc_strategy,
109 bc_open,
110 bc_close,
111 noioctl,
112 bc_print,
113 NULL
117 * Translate between BIOS device numbers and our private unit numbers.
120 bc_bios2unit(int biosdev)
122 int i;
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)
128 return(i);
130 return(-1);
134 bc_unit2bios(int unit)
136 if ((unit >= 0) && (unit < nbcinfo))
137 return(bcinfo[unit].bc_unit);
138 return(-1);
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.
146 static int
147 bc_init(void)
150 return (0);
154 bc_add(int biosdev)
157 if (nbcinfo >= MAXBCDEV)
158 return (-1);
159 bcinfo[nbcinfo].bc_unit = biosdev;
160 v86.ctl = V86_FLAGS;
161 v86.addr = 0x13;
162 v86.eax = 0x4b01;
163 v86.edx = biosdev;
164 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
165 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
166 v86int();
167 if ((v86.eax & 0xff00) != 0)
168 return (-1);
170 printf("BIOS CD is cd%d\n", nbcinfo);
171 nbcinfo++;
172 bcache_add_dev(nbcinfo); /* register cd device in bcache */
173 return(0);
177 * Print information about disks
179 static int
180 bc_print(int verbose)
182 char line[80];
183 int i, ret = 0;
185 if (nbcinfo == 0)
186 return (0);
188 printf("%s devices:", bioscd.dv_name);
189 if ((ret = pager_output("\n")) != 0)
190 return (ret);
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);
196 if (ret != 0)
197 return (ret);
199 return (ret);
203 * Attempt to open the disk described by (dev) for use by (f).
205 static int
206 bc_open(struct open_file *f, ...)
208 va_list ap;
209 struct i386_devdesc *dev;
211 va_start(ap, f);
212 dev = va_arg(ap, struct i386_devdesc *);
213 va_end(ap);
214 if (dev->d_unit >= nbcinfo) {
215 DEBUG("attempt to open nonexistent disk");
216 return(ENXIO);
219 BC(dev).bc_open++;
220 if (BC(dev).bc_bcache == NULL)
221 BC(dev).bc_bcache = bcache_allocate();
222 return(0);
225 static int
226 bc_close(struct open_file *f)
228 struct i386_devdesc *dev;
230 dev = (struct i386_devdesc *)f->f_devdata;
231 BC(dev).bc_open--;
232 if (BC(dev).bc_open == 0) {
233 bcache_free(BC(dev).bc_bcache);
234 BC(dev).bc_bcache = NULL;
236 return(0);
239 static int
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));
254 static int
255 bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
256 char *buf, size_t *rsize)
258 struct i386_devdesc *dev;
259 int unit;
260 int blks;
261 #ifdef BD_SUPPORT_FRAGS
262 char fragbuf[BIOSCD_SECSIZE];
263 size_t fragsize;
265 fragsize = size % BIOSCD_SECSIZE;
266 #else
267 if (size % BIOSCD_SECSIZE)
268 return (EINVAL);
269 #endif
271 if ((rw & F_MASK) != F_READ)
272 return(EROFS);
273 dev = (struct i386_devdesc *)devdata;
274 unit = dev->d_unit;
275 blks = size / BIOSCD_SECSIZE;
276 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
277 return (EINVAL);
278 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
279 DEBUG("read %d from %lld to %p", blks, dblk, buf);
281 if (rsize)
282 *rsize = 0;
283 if ((blks = bc_read(unit, dblk, blks, buf)) < 0) {
284 DEBUG("read error");
285 return (EIO);
286 } else {
287 if (size / BIOSCD_SECSIZE > blks) {
288 if (rsize)
289 *rsize = blks * BIOSCD_SECSIZE;
290 return (0);
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) {
297 if (blks) {
298 if (rsize)
299 *rsize = blks * BIOSCD_SECSIZE;
300 return (0);
302 DEBUG("frag read error");
303 return(EIO);
305 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
306 #endif
307 if (rsize)
308 *rsize = size;
309 return (0);
312 /* return negative value for an error, otherwise blocks read */
313 static int
314 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
316 u_int maxfer, resid, result, retry, x;
317 caddr_t bbuf, p, xp;
318 static struct edd_packet packet;
319 int biosdev;
320 #ifdef DISK_DEBUG
321 int error;
322 #endif
324 /* Just in case some idiot actually tries to read -1 blocks... */
325 if (blks < 0)
326 return (-1);
328 /* If nothing to do, just return succcess. */
329 if (blks == 0)
330 return (0);
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
337 * bounce buffer.
339 x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE;
340 if (x == 0)
341 panic("BUG: Real mode buffer is too small\n");
342 x = min(x, (unsigned)blks);
343 bbuf = PTOV(V86_IO_BUFFER);
344 maxfer = x;
345 } else {
346 bbuf = NULL;
347 maxfer = 0;
350 biosdev = bc_unit2bios(unit);
351 resid = blks;
352 p = dest;
354 while (resid > 0) {
355 if (bbuf)
356 xp = bbuf;
357 else
358 xp = p;
359 x = resid;
360 if (maxfer > 0)
361 x = min(x, maxfer);
364 * Loop retrying the operation a couple of times. The BIOS
365 * may also retry.
367 for (retry = 0; retry < 3; retry++) {
368 /* If retrying, reset the drive */
369 if (retry > 0) {
370 v86.ctl = V86_FLAGS;
371 v86.addr = 0x13;
372 v86.eax = 0;
373 v86.edx = biosdev;
374 v86int();
377 packet.len = sizeof(struct edd_packet);
378 packet.count = x;
379 packet.off = VTOPOFF(xp);
380 packet.seg = VTOPSEG(xp);
381 packet.lba = dblk;
382 v86.ctl = V86_FLAGS;
383 v86.addr = 0x13;
384 v86.eax = 0x4200;
385 v86.edx = biosdev;
386 v86.ds = VTOPSEG(&packet);
387 v86.esi = VTOPOFF(&packet);
388 v86int();
389 result = V86_CY(v86.efl);
390 if (result == 0)
391 break;
392 /* fall back to 1 sector read */
393 x = 1;
396 #ifdef DISK_DEBUG
397 error = (v86.eax >> 8) & 0xff;
398 #endif
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 */
404 if (result != 0)
405 break;
407 if (bbuf != NULL)
408 bcopy(bbuf, p, x * BIOSCD_SECSIZE);
409 p += (x * BIOSCD_SECSIZE);
410 dblk += x;
411 resid -= x;
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)
428 int biosdev, unit;
429 int major;
430 int rootdev;
432 unit = dev->d_unit;
433 biosdev = bc_unit2bios(unit);
434 DEBUG("unit %d BIOS device %d", unit, biosdev);
435 if (biosdev == -1) /* not a BIOS device */
436 return(-1);
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
445 * the boot device.
447 major = ACDMAJOR;
448 unit = 0; /* XXX */
450 /* XXX: Assume partition 'a'. */
451 rootdev = MAKEBOOTDEV(major, 0, unit, 0);
452 DEBUG("dev is 0x%x\n", rootdev);
453 return(rootdev);