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
46 #include <stdarg.h>
47
48 #include <bootstrap.h>
49 #include <btxv86.h>
50 #include <edd.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
bc_bios2unit(int biosdev)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
bc_unit2bios(int unit)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
bc_init(void)142 bc_init(void)
143 {
144
145 return (0);
146 }
147
148 int
bc_add(int biosdev)149 bc_add(int biosdev)
150 {
151
152 if (nbcinfo >= MAXBCDEV)
153 return (-1);
154 bcinfo[nbcinfo].bc_unit = biosdev;
155 v86.ctl = V86_FLAGS;
156 v86.addr = 0x13;
157 v86.eax = 0x4b01;
158 v86.edx = biosdev;
159 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
160 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
161 v86int();
162 if ((v86.eax & 0xff00) != 0)
163 return (-1);
164
165 printf("BIOS CD is cd%d\n", nbcinfo);
166 nbcinfo++;
167 return(0);
168 }
169
170 /*
171 * Print information about disks
172 */
173 static void
bc_print(int verbose)174 bc_print(int verbose)
175 {
176 char line[80];
177 int i;
178
179 for (i = 0; i < nbcinfo; i++) {
180 sprintf(line, " cd%d: Device 0x%x\n", i,
181 bcinfo[i].bc_sp.sp_devicespec);
182 pager_output(line);
183 }
184 }
185
186 /*
187 * Attempt to open the disk described by (dev) for use by (f).
188 */
189 static int
bc_open(struct open_file * f,...)190 bc_open(struct open_file *f, ...)
191 {
192 va_list ap;
193 struct i386_devdesc *dev;
194
195 va_start(ap, f);
196 dev = va_arg(ap, struct i386_devdesc *);
197 va_end(ap);
198 if (dev->d_unit >= nbcinfo) {
199 DEBUG("attempt to open nonexistent disk");
200 return(ENXIO);
201 }
202
203 return(0);
204 }
205
206 static int
bc_close(struct open_file * f)207 bc_close(struct open_file *f)
208 {
209
210 return(0);
211 }
212
213 static int
bc_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)214 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
215 size_t *rsize)
216 {
217 struct i386_devdesc *dev;
218 int unit;
219 int blks;
220 #ifdef BD_SUPPORT_FRAGS
221 char fragbuf[BIOSCD_SECSIZE];
222 size_t fragsize;
223
224 fragsize = size % BIOSCD_SECSIZE;
225 #else
226 if (size % BIOSCD_SECSIZE)
227 return (EINVAL);
228 #endif
229
230 if (rw != F_READ)
231 return(EROFS);
232 dev = (struct i386_devdesc *)devdata;
233 unit = dev->d_unit;
234 blks = size / BIOSCD_SECSIZE;
235 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
236 return (EINVAL);
237 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
238 DEBUG("read %d from %lld to %p", blks, dblk, buf);
239
240 if (rsize)
241 *rsize = 0;
242 if (blks && bc_read(unit, dblk, blks, buf)) {
243 DEBUG("read error");
244 return (EIO);
245 }
246 #ifdef BD_SUPPORT_FRAGS
247 DEBUG("frag read %d from %lld+%d to %p",
248 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
249 if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
250 DEBUG("frag read error");
251 return(EIO);
252 }
253 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
254 #endif
255 if (rsize)
256 *rsize = size;
257 return (0);
258 }
259
260 /* Max number of sectors to bounce-buffer at a time. */
261 #define CD_BOUNCEBUF 8
262
263 static int
bc_read(int unit,daddr_t dblk,int blks,caddr_t dest)264 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
265 {
266 u_int maxfer, resid, result, retry, x;
267 caddr_t bbuf, p, xp;
268 static struct edd_packet packet;
269 int biosdev;
270 #ifdef DISK_DEBUG
271 int error;
272 #endif
273
274 /* Just in case some idiot actually tries to read -1 blocks... */
275 if (blks < 0)
276 return (-1);
277
278 /* If nothing to do, just return succcess. */
279 if (blks == 0)
280 return (0);
281
282 /* Decide whether we have to bounce */
283 if (VTOP(dest) >> 20 != 0) {
284 /*
285 * The destination buffer is above first 1MB of
286 * physical memory so we have to arrange a suitable
287 * bounce buffer.
288 */
289 x = min(CD_BOUNCEBUF, (unsigned)blks);
290 bbuf = alloca(x * BIOSCD_SECSIZE);
291 maxfer = x;
292 } else {
293 bbuf = NULL;
294 maxfer = 0;
295 }
296
297 biosdev = bc_unit2bios(unit);
298 resid = blks;
299 p = dest;
300
301 while (resid > 0) {
302 if (bbuf)
303 xp = bbuf;
304 else
305 xp = p;
306 x = resid;
307 if (maxfer > 0)
308 x = min(x, maxfer);
309
310 /*
311 * Loop retrying the operation a couple of times. The BIOS
312 * may also retry.
313 */
314 for (retry = 0; retry < 3; retry++) {
315 /* If retrying, reset the drive */
316 if (retry > 0) {
317 v86.ctl = V86_FLAGS;
318 v86.addr = 0x13;
319 v86.eax = 0;
320 v86.edx = biosdev;
321 v86int();
322 }
323
324 packet.len = sizeof(struct edd_packet);
325 packet.count = x;
326 packet.off = VTOPOFF(xp);
327 packet.seg = VTOPSEG(xp);
328 packet.lba = dblk;
329 v86.ctl = V86_FLAGS;
330 v86.addr = 0x13;
331 v86.eax = 0x4200;
332 v86.edx = biosdev;
333 v86.ds = VTOPSEG(&packet);
334 v86.esi = VTOPOFF(&packet);
335 v86int();
336 result = V86_CY(v86.efl);
337 if (result == 0)
338 break;
339 }
340
341 #ifdef DISK_DEBUG
342 error = (v86.eax >> 8) & 0xff;
343 #endif
344 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
345 VTOP(p), result ? "failed" : "ok");
346 DEBUG("unit %d status 0x%x", unit, error);
347 if (bbuf != NULL)
348 bcopy(bbuf, p, x * BIOSCD_SECSIZE);
349 p += (x * BIOSCD_SECSIZE);
350 dblk += x;
351 resid -= x;
352 }
353
354 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
355 return(0);
356 }
357
358 /*
359 * Return a suitable dev_t value for (dev).
360 */
361 int
bc_getdev(struct i386_devdesc * dev)362 bc_getdev(struct i386_devdesc *dev)
363 {
364 int biosdev, unit;
365 int major;
366 int rootdev;
367
368 unit = dev->d_unit;
369 biosdev = bc_unit2bios(unit);
370 DEBUG("unit %d BIOS device %d", unit, biosdev);
371 if (biosdev == -1) /* not a BIOS device */
372 return(-1);
373
374 /*
375 * XXX: Need to examine device spec here to figure out if SCSI or
376 * ATAPI. No idea on how to figure out device number. All we can
377 * really pass to the kernel is what bus and device on which bus we
378 * were booted from, which dev_t isn't well suited to since those
379 * number don't match to unit numbers very well. We may just need
380 * to engage in a hack where we pass -C to the boot args if we are
381 * the boot device.
382 */
383 major = ACDMAJOR;
384 unit = 0; /* XXX */
385
386 /* XXX: Assume partition 'a'. */
387 rootdev = MAKEBOOTDEV(major, 0, unit, 0);
388 DEBUG("dev is 0x%x\n", rootdev);
389 return(rootdev);
390 }
391