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 * $FreeBSD: src/sys/boot/i386/libi386/bioscd.c,v 1.5 2003/08/25 23:28:31 obrien Exp $
28 * $DragonFly: src/sys/boot/pc32/libi386/bioscd.c,v 1.6 2006/09/10 01:26:31 dillon Exp $
29 */
30 /*
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.
33 *
34 * Ideas and algorithms from:
35 *
36 * - FreeBSD libi386/biosdisk.c
37 *
38 */
39
40 #include <stand.h>
41
42 #include <sys/param.h>
43 #include <machine/bootinfo.h>
44 #include <machine/psl.h>
45
46 #include <stdarg.h>
47
48 #include <bootstrap.h>
49 #include <btxv86.h>
50 #include "libi386.h"
51
52 #define BIOSCD_SECSIZE 2048
53 #define BUFSIZE (1 * BIOSCD_SECSIZE)
54 #define MAXBCDEV 1
55
56 /* Major numbers for devices we frontend for. */
57 #define ACDMAJOR 117
58 #define CDMAJOR 15
59
60 #ifdef DISK_DEBUG
61 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
62 #else
63 # define DEBUG(fmt, args...)
64 #endif
65
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;
78 };
79
80 /*
81 * List of BIOS devices, translation from disk unit number to
82 * BIOS unit number.
83 */
84 static struct bcinfo {
85 int bc_unit; /* BIOS unit number */
86 struct specification_packet bc_sp;
87 } bcinfo [MAXBCDEV];
88 static int nbcinfo = 0;
89
90 static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
91 static int bc_init(void);
92 static int bc_strategy(void *devdata, int flag, daddr_t dblk,
93 size_t size, char *buf, size_t *rsize);
94 static int bc_open(struct open_file *f, ...);
95 static int bc_close(struct open_file *f);
96 static void bc_print(int verbose);
97
98 struct devsw bioscd = {
99 "cd",
100 DEVT_CD,
101 bc_init,
102 bc_strategy,
103 bc_open,
104 bc_close,
105 noioctl,
106 bc_print,
107 NULL
108 };
109
110 /*
111 * Translate between BIOS device numbers and our private unit numbers.
112 */
113 int
bc_bios2unit(int biosdev)114 bc_bios2unit(int biosdev)
115 {
116 int i;
117
118 DEBUG("looking for bios device 0x%x", biosdev);
119 for (i = 0; i < nbcinfo; i++) {
120 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
121 if (bcinfo[i].bc_unit == biosdev)
122 return(i);
123 }
124 return(-1);
125 }
126
127 int
bc_unit2bios(int unit)128 bc_unit2bios(int unit)
129 {
130 if ((unit >= 0) && (unit < nbcinfo))
131 return(bcinfo[unit].bc_unit);
132 return(-1);
133 }
134
135 /*
136 * We can't quiz, we have to be told what device to use, so this functoin
137 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS
138 * device number to add.
139 */
140 static int
bc_init(void)141 bc_init(void)
142 {
143
144 return (0);
145 }
146
147 int
bc_add(int biosdev)148 bc_add(int biosdev)
149 {
150
151 if (nbcinfo >= MAXBCDEV)
152 return (-1);
153 bcinfo[nbcinfo].bc_unit = biosdev;
154 v86.ctl = V86_FLAGS;
155 v86.addr = 0x13;
156 v86.eax = 0x4b01;
157 v86.edx = biosdev;
158 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
159 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
160 v86int();
161 if ((v86.eax & 0xff00) != 0)
162 return (-1);
163
164 printf("BIOS CD is cd%d\n", nbcinfo);
165 nbcinfo++;
166 return(0);
167 }
168
169 /*
170 * Print information about disks
171 */
172 static void
bc_print(int verbose)173 bc_print(int verbose)
174 {
175 int i;
176 char line[80];
177
178 for (i = 0; i < nbcinfo; i++) {
179 sprintf(line, " cd%d: Device 0x%x\n", i,
180 bcinfo[i].bc_sp.sp_devicespec);
181 pager_output(line);
182 }
183 }
184
185 /*
186 * Attempt to open the disk described by (dev) for use by (f).
187 */
188 static int
bc_open(struct open_file * f,...)189 bc_open(struct open_file *f, ...)
190 {
191 va_list ap;
192 struct i386_devdesc *dev;
193
194 va_start(ap, f);
195 dev = va_arg(ap, struct i386_devdesc *);
196 va_end(ap);
197 if (dev->d_kind.bioscd.unit >= nbcinfo) {
198 DEBUG("attempt to open nonexistent disk");
199 return(ENXIO);
200 }
201
202 return(0);
203 }
204
205 static int
bc_close(struct open_file * f)206 bc_close(struct open_file *f)
207 {
208
209 return(0);
210 }
211
212 static int
bc_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)213 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
214 size_t *rsize)
215 {
216 struct i386_devdesc *dev;
217 int unit;
218 int blks;
219 #ifdef BD_SUPPORT_FRAGS
220 #error "xxx broken code xxx"
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_kind.bioscd.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 %d 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("bc_strategy: frag read %d from %d+%d to %p",
248 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
249 if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
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 static int
bc_read(int unit,daddr_t dblk,int blks,caddr_t dest)261 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
262 {
263 u_int result, retry;
264 static unsigned short packet[8];
265 int biosdev;
266 int n;
267 #ifdef DISK_DEBUG
268 int error;
269 #endif
270
271 /* Just in case some idiot actually tries to read -1 blocks... */
272 if (blks < 0)
273 return (-1);
274
275 /* If nothing to do, just return succcess. */
276 if (blks == 0)
277 return (0);
278
279 biosdev = bc_unit2bios(unit);
280 /*
281 * Loop retrying the operation a couple of times. The BIOS
282 * may also retry.
283 */
284 for (retry = 0; retry < 3; retry++) {
285 /* If retrying, reset the drive */
286 if (retry > 0) {
287 v86.ctl = V86_FLAGS;
288 v86.addr = 0x13;
289 v86.eax = 0;
290 v86.edx = biosdev;
291 v86int();
292 }
293
294 n = BOUNCEBUF_SIZE / BIOSCD_SECSIZE;
295 if (n > blks)
296 n = blks;
297
298 packet[0] = 0x10;
299 packet[1] = n;
300 packet[2] = VTOPOFF(bounce_base);
301 packet[3] = VTOPSEG(bounce_base);
302 packet[4] = dblk & 0xffff;
303 packet[5] = dblk >> 16;
304 packet[6] = 0;
305 packet[7] = 0;
306 v86.ctl = V86_FLAGS;
307 v86.addr = 0x13;
308 v86.eax = 0x4200;
309 v86.edx = biosdev;
310 v86.ds = VTOPSEG(packet);
311 v86.esi = VTOPOFF(packet);
312 v86int();
313 result = (v86.efl & PSL_C);
314 if (result == 0) {
315 bcopy(bounce_base, dest, n * BIOSCD_SECSIZE);
316 blks -= n;
317 dest += n * BIOSCD_SECSIZE;
318 if (blks == 0)
319 break;
320 retry = 0;
321 }
322 }
323
324 #ifdef DISK_DEBUG
325 error = (v86.eax >> 8) & 0xff;
326 #endif
327 DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
328 VTOP(dest), result ? "failed" : "ok");
329 DEBUG("unit %d status 0x%x", unit, error);
330
331 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
332 return(0);
333 }
334
335 /*
336 * Return a suitable cdev_t value for (dev).
337 */
338 int
bc_getdev(struct i386_devdesc * dev)339 bc_getdev(struct i386_devdesc *dev)
340 {
341 int biosdev, unit;
342 int major;
343 int rootdev;
344
345 unit = dev->d_kind.bioscd.unit;
346 biosdev = bc_unit2bios(unit);
347 DEBUG("unit %d BIOS device %d", unit, biosdev);
348 if (biosdev == -1) /* not a BIOS device */
349 return(-1);
350
351 /*
352 * XXX: Need to examine device spec here to figure out if SCSI or
353 * ATAPI. No idea on how to figure out device number. All we can
354 * really pass to the kernel is what bus and device on which bus we
355 * were booted from, which cdev_t isn't well suited to since those
356 * number don't match to unit numbers very well. We may just need
357 * to engage in a hack where we pass -C to the boot args if we are
358 * the boot device.
359 */
360 major = ACDMAJOR;
361 unit = 0; /* XXX */
362
363 /* XXX: Assume partition 'a'. */
364 rootdev = MAKEBOOTDEV(major, 0, unit, 0);
365 DEBUG("dev is 0x%x\n", rootdev);
366 return(rootdev);
367 }
368