Fully synchronize sys/boot from FreeBSD-5.x, but add / to the module path
[dragonfly.git] / sys / boot / pc32 / libi386 / bioscd.c
CommitLineData
984263bc
MD
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 *
5ee58eed
MD
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.4 2003/11/10 06:08:36 dillon Exp $
984263bc 29 */
984263bc
MD
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
5ee58eed
MD
42#include <sys/param.h>
43#include <machine/bootinfo.h>
984263bc
MD
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
66struct 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 */
84static struct bcinfo {
85 int bc_unit; /* BIOS unit number */
86 struct specification_packet bc_sp;
87} bcinfo [MAXBCDEV];
88static int nbcinfo = 0;
89
90static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
91static int bc_init(void);
92static int bc_strategy(void *devdata, int flag, daddr_t dblk,
93 size_t size, char *buf, size_t *rsize);
94static int bc_realstrategy(void *devdata, int flag, daddr_t dblk,
95 size_t size, char *buf, size_t *rsize);
96static int bc_open(struct open_file *f, ...);
97static int bc_close(struct open_file *f);
98static void bc_print(int verbose);
99
100struct devsw bioscd = {
101 "cd",
102 DEVT_CD,
103 bc_init,
104 bc_strategy,
105 bc_open,
106 bc_close,
107 noioctl,
108 bc_print,
109 NULL
110};
111
112/*
113 * Translate between BIOS device numbers and our private unit numbers.
114 */
115int
116bc_bios2unit(int biosdev)
117{
118 int i;
119
120 DEBUG("looking for bios device 0x%x", biosdev);
121 for (i = 0; i < nbcinfo; i++) {
122 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
123 if (bcinfo[i].bc_unit == biosdev)
124 return(i);
125 }
126 return(-1);
127}
128
129int
130bc_unit2bios(int unit)
131{
132 if ((unit >= 0) && (unit < nbcinfo))
133 return(bcinfo[unit].bc_unit);
134 return(-1);
135}
136
137/*
138 * We can't quiz, we have to be told what device to use, so this functoin
139 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS
140 * device number to add.
141 */
142static int
143bc_init(void)
144{
145
146 return (0);
147}
148
149int
150bc_add(int biosdev)
151{
152
153 if (nbcinfo >= MAXBCDEV)
154 return (-1);
155 bcinfo[nbcinfo].bc_unit = biosdev;
156 v86.ctl = V86_FLAGS;
157 v86.addr = 0x13;
158 v86.eax = 0x4b01;
159 v86.edx = biosdev;
160 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
161 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
162 v86int();
163 if ((v86.eax & 0xff00) != 0)
164 return (-1);
165
166 printf("BIOS CD is cd%d\n", nbcinfo);
167 nbcinfo++;
168 return(0);
169}
170
171/*
172 * Print information about disks
173 */
174static void
175bc_print(int verbose)
176{
177 int i;
178 char line[80];
179
180 for (i = 0; i < nbcinfo; i++) {
181 sprintf(line, " cd%d: Device 0x%x\n", i,
182 bcinfo[i].bc_sp.sp_devicespec);
183 pager_output(line);
184 }
185}
186
187/*
188 * Attempt to open the disk described by (dev) for use by (f).
189 */
190static int
191bc_open(struct open_file *f, ...)
192{
5ee58eed 193 va_list ap;
984263bc
MD
194 struct i386_devdesc *dev;
195 int error;
196
5ee58eed
MD
197 va_start(ap, f);
198 dev = va_arg(ap, struct i386_devdesc *);
199 va_end(ap);
984263bc
MD
200 if (dev->d_kind.bioscd.unit >= nbcinfo) {
201 DEBUG("attempt to open nonexistent disk");
202 return(ENXIO);
203 }
204
205 return(0);
206}
207
208static int
209bc_close(struct open_file *f)
210{
211
212 return(0);
213}
214
215static int
216bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
217 size_t *rsize)
218{
219 struct i386_devdesc *dev;
220 int unit;
221 int blks;
222#ifdef BD_SUPPORT_FRAGS
223 char fragbuf[BIOSCD_SECSIZE];
224 size_t fragsize;
225
226 fragsize = size % BIOSCD_SECSIZE;
227#else
228 if (size % BIOSCD_SECSIZE)
229 return (EINVAL);
230#endif
231
232 if (rw != F_READ)
233 return(EROFS);
234 dev = (struct i386_devdesc *)devdata;
235 unit = dev->d_kind.bioscd.unit;
236 blks = size / BIOSCD_SECSIZE;
237 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
238 return (EINVAL);
239 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
240 DEBUG("read %d from %d to %p", blks, dblk, buf);
241
242 if (rsize)
243 *rsize = 0;
244 if (blks && bc_read(unit, dblk, blks, buf)) {
245 DEBUG("read error");
246 return (EIO);
247 }
248#ifdef BD_SUPPORT_FRAGS
249 DEBUG("bc_strategy: frag read %d from %d+%d to %p",
250 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
251 if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
252 DEBUG("frag read error");
253 return(EIO);
254 }
255 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
256#endif
257 if (rsize)
258 *rsize = size;
259 return (0);
260}
261
262static int
263bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
264{
265 u_int result, resid, retry;
266 static unsigned short packet[8];
267 int biosdev;
268#ifdef DISK_DEBUG
269 int error;
270#endif
271
272 /* Just in case some idiot actually tries to read -1 blocks... */
273 if (blks < 0)
274 return (-1);
275
276 /* If nothing to do, just return succcess. */
277 if (blks == 0)
278 return (0);
279
280 biosdev = bc_unit2bios(unit);
281 /*
282 * Loop retrying the operation a couple of times. The BIOS
283 * may also retry.
284 */
285 for (retry = 0; retry < 3; retry++) {
286 /* If retrying, reset the drive */
287 if (retry > 0) {
288 v86.ctl = V86_FLAGS;
289 v86.addr = 0x13;
290 v86.eax = 0;
291 v86.edx = biosdev;
292 v86int();
293 }
294
295 packet[0] = 0x10;
296 packet[1] = blks;
297 packet[2] = VTOPOFF(dest);
298 packet[3] = VTOPSEG(dest);
299 packet[4] = dblk & 0xffff;
300 packet[5] = dblk >> 16;
301 packet[6] = 0;
302 packet[7] = 0;
303 v86.ctl = V86_FLAGS;
304 v86.addr = 0x13;
305 v86.eax = 0x4200;
306 v86.edx = biosdev;
307 v86.ds = VTOPSEG(packet);
308 v86.esi = VTOPOFF(packet);
309 v86int();
310 result = (v86.efl & PSL_C);
311 if (result == 0)
312 break;
313 }
314
315#ifdef DISK_DEBUG
316 error = (v86.eax >> 8) & 0xff;
317#endif
318 DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
319 VTOP(dest), result ? "failed" : "ok");
320 DEBUG("unit %d status 0x%x", unit, error);
321
322/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
323 return(0);
324}
325
326/*
327 * Return a suitable dev_t value for (dev).
328 */
329int
330bc_getdev(struct i386_devdesc *dev)
331{
332 int biosdev, unit;
333 int major;
334 int rootdev;
335
336 unit = dev->d_kind.bioscd.unit;
337 biosdev = bc_unit2bios(unit);
338 DEBUG("unit %d BIOS device %d", unit, biosdev);
339 if (biosdev == -1) /* not a BIOS device */
340 return(-1);
341
342 /*
343 * XXX: Need to examine device spec here to figure out if SCSI or
344 * ATAPI. No idea on how to figure out device number. All we can
345 * really pass to the kernel is what bus and device on which bus we
346 * were booted from, which dev_t isn't well suited to since those
347 * number don't match to unit numbers very well. We may just need
348 * to engage in a hack where we pass -C to the boot args if we are
349 * the boot device.
350 */
351 major = ACDMAJOR;
352 unit = 0; /* XXX */
353
354 /* XXX: Assume partition 'a'. */
355 rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
356 DEBUG("dev is 0x%x\n", rootdev);
357 return(rootdev);
358}