xref: /trueos/sys/boot/i386/libi386/bioscd.c (revision f68bada80b9371e881d04b43a8ab412fa54cd5fa)
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