xref: /openbsd-src/sys/arch/amd64/stand/libsa/biosdev.c (revision 36dba039b91cf453f3b66c370c50b00c9aa5378e)
1*36dba039Sjsg /*	$OpenBSD: biosdev.c,v 1.37 2024/04/14 03:26:25 jsg Exp $	*/
2a47f7207Smickey 
3a47f7207Smickey /*
4a47f7207Smickey  * Copyright (c) 1996 Michael Shalayeff
5a47f7207Smickey  * Copyright (c) 2003 Tobias Weingartner
6a47f7207Smickey  * All rights reserved.
7a47f7207Smickey  *
8a47f7207Smickey  * Redistribution and use in source and binary forms, with or without
9a47f7207Smickey  * modification, are permitted provided that the following conditions
10a47f7207Smickey  * are met:
11a47f7207Smickey  * 1. Redistributions of source code must retain the above copyright
12a47f7207Smickey  *    notice, this list of conditions and the following disclaimer.
13a47f7207Smickey  * 2. Redistributions in binary form must reproduce the above copyright
14a47f7207Smickey  *    notice, this list of conditions and the following disclaimer in the
15a47f7207Smickey  *    documentation and/or other materials provided with the distribution.
16a47f7207Smickey  *
17a47f7207Smickey  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18a47f7207Smickey  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19a47f7207Smickey  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a47f7207Smickey  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21a47f7207Smickey  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a47f7207Smickey  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a47f7207Smickey  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a47f7207Smickey  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a47f7207Smickey  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a47f7207Smickey  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a47f7207Smickey  * SUCH DAMAGE.
28a47f7207Smickey  *
29a47f7207Smickey  */
30a47f7207Smickey 
31a47f7207Smickey #include <sys/param.h>
32a47f7207Smickey #include <sys/reboot.h>
33a47f7207Smickey #include <sys/disklabel.h>
34e995780eSjsing #include <isofs/cd9660/iso.h>
35e995780eSjsing #include <lib/libsa/saerrno.h>
36a47f7207Smickey #include <machine/tss.h>
37a47f7207Smickey #include <machine/biosvar.h>
3870197c51Sjsing 
3970197c51Sjsing #include "biosdev.h"
40a47f7207Smickey #include "disk.h"
41a47f7207Smickey #include "libsa.h"
4270197c51Sjsing 
4392befcddSjsing #ifdef SOFTRAID
4492befcddSjsing #include <dev/softraidvar.h>
45e876def9Sjsing #include <lib/libsa/softraid.h>
4665f4a3c7Sjsing #include "softraid_amd64.h"
4770197c51Sjsing #endif
48a47f7207Smickey 
49a47f7207Smickey static const char *biosdisk_err(u_int);
50a47f7207Smickey static int biosdisk_errno(u_int);
51a47f7207Smickey 
523830afd3Sderaadt int CHS_rw (int, int, int, int, int, int, void *);
539730fd37Skrw static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
54a47f7207Smickey 
557b5f1cbeSyasuoka static int biosd_io(int, bios_diskinfo_t *, u_int, int, void *);
56fb9d9d2cSkrw static u_int findopenbsd(bios_diskinfo_t *, const char **);
57d173b221Sreyk 
58a47f7207Smickey extern int debug;
59a47f7207Smickey int bios_bootdev;
60338feb3aStom int bios_cddev = -1;		/* Set by srt0 if coming from CD */
61a47f7207Smickey 
62a47f7207Smickey struct EDD_CB {
63a47f7207Smickey 	u_int8_t  edd_len;	/* size of packet */
64a47f7207Smickey 	u_int8_t  edd_res1;	/* reserved */
65a47f7207Smickey 	u_int8_t  edd_nblk;	/* # of blocks to transfer */
66a47f7207Smickey 	u_int8_t  edd_res2;	/* reserved */
67a47f7207Smickey 	u_int16_t edd_off;	/* address of buffer (offset) */
68a47f7207Smickey 	u_int16_t edd_seg;	/* address of buffer (segment) */
69a47f7207Smickey 	u_int64_t edd_daddr;	/* starting block */
70a47f7207Smickey };
71a47f7207Smickey 
72a47f7207Smickey /*
73a47f7207Smickey  * reset disk system
74a47f7207Smickey  */
75a47f7207Smickey static int
biosdreset(int dev)76a47f7207Smickey biosdreset(int dev)
77a47f7207Smickey {
78a47f7207Smickey 	int rv;
79c58c0b92Skrw 
802df76cc2Sguenther 	__asm volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
81a47f7207Smickey 	    : "0" (0), "d" (dev) : "%ecx", "cc");
82c58c0b92Skrw 
83c58c0b92Skrw 	return ((rv & 0xff)? rv >> 8 : 0);
84a47f7207Smickey }
85a47f7207Smickey 
86a47f7207Smickey /*
87a47f7207Smickey  * Fill out a bios_diskinfo_t for this device.
88a47f7207Smickey  * Return 0 if all ok.
89a47f7207Smickey  * Return 1 if not ok.
90a47f7207Smickey  */
91a47f7207Smickey int
bios_getdiskinfo(int dev,bios_diskinfo_t * pdi)92a47f7207Smickey bios_getdiskinfo(int dev, bios_diskinfo_t *pdi)
93a47f7207Smickey {
94a47f7207Smickey 	u_int rv;
95a47f7207Smickey 
96a47f7207Smickey 	/* Just reset, don't check return code */
97a47f7207Smickey 	rv = biosdreset(dev);
98a47f7207Smickey 
99a47f7207Smickey #ifdef BIOS_DEBUG
100a47f7207Smickey 	if (debug)
101a47f7207Smickey 		printf("getinfo: try #8, 0x%x, %p\n", dev, pdi);
102a47f7207Smickey #endif
1032df76cc2Sguenther 	__asm volatile (DOINT(0x13) "\n\t"
104a47f7207Smickey 	    "setc %b0; movzbl %h1, %1\n\t"
105a47f7207Smickey 	    "movzbl %%cl, %3; andb $0x3f, %b3\n\t"
106a47f7207Smickey 	    "xchgb %%cl, %%ch; rolb $2, %%ch"
107a47f7207Smickey 	    : "=a" (rv), "=d" (pdi->bios_heads),
108a47f7207Smickey 	      "=c" (pdi->bios_cylinders),
109a47f7207Smickey 	      "=b" (pdi->bios_sectors)
110a47f7207Smickey 	    : "0" (0x0800), "1" (dev) : "cc");
111a47f7207Smickey 
112a47f7207Smickey #ifdef BIOS_DEBUG
113a47f7207Smickey 	if (debug) {
114a47f7207Smickey 		printf("getinfo: got #8\n");
115a47f7207Smickey 		printf("disk 0x%x: %d,%d,%d\n", dev, pdi->bios_cylinders,
116a47f7207Smickey 		    pdi->bios_heads, pdi->bios_sectors);
117a47f7207Smickey 	}
118a47f7207Smickey #endif
119a47f7207Smickey 	if (rv & 0xff)
120c58c0b92Skrw 		return 1;
121a47f7207Smickey 
122a47f7207Smickey 	/* Fix up info */
123a47f7207Smickey 	pdi->bios_number = dev;
124a47f7207Smickey 	pdi->bios_heads++;
125a47f7207Smickey 	pdi->bios_cylinders &= 0x3ff;
126a47f7207Smickey 	pdi->bios_cylinders++;
127a47f7207Smickey 
128a47f7207Smickey 	/* NOTE:
129a47f7207Smickey 	 * This currently hangs/reboots some machines
130b5d602aeSjmc 	 * The IBM ThinkPad 750ED for one.
131a47f7207Smickey 	 *
132a47f7207Smickey 	 * Funny that an IBM/MS extension would not be
133a47f7207Smickey 	 * implemented by an IBM system...
134a47f7207Smickey 	 *
135a47f7207Smickey 	 * Future hangs (when reported) can be "fixed"
136a47f7207Smickey 	 * with getSYSCONFaddr() and an exceptions list.
137a47f7207Smickey 	 */
138a47f7207Smickey 	if (dev & 0x80 && (dev == 0x80 || dev == 0x81 || dev == bios_bootdev)) {
139a47f7207Smickey 		int bm;
140a47f7207Smickey 
141a47f7207Smickey #ifdef BIOS_DEBUG
142a47f7207Smickey 		if (debug)
143a47f7207Smickey 			printf("getinfo: try #41, 0x%x\n", dev);
144a47f7207Smickey #endif
145a47f7207Smickey 		/* EDD support check */
1462df76cc2Sguenther 		__asm volatile(DOINT(0x13) "; setc %b0"
147a47f7207Smickey 			 : "=a" (rv), "=c" (bm)
148a47f7207Smickey 			 : "0" (0x4100), "b" (0x55aa), "d" (dev) : "cc");
149a47f7207Smickey 		if (!(rv & 0xff) && (BIOS_regs.biosr_bx & 0xffff) == 0xaa55)
150a47f7207Smickey 			pdi->bios_edd = (bm & 0xffff) | ((rv & 0xff) << 16);
151a47f7207Smickey 		else
152a47f7207Smickey 			pdi->bios_edd = -1;
153a47f7207Smickey 
154a47f7207Smickey #ifdef BIOS_DEBUG
155a47f7207Smickey 		if (debug) {
156a47f7207Smickey 			printf("getinfo: got #41\n");
157a47f7207Smickey 			printf("disk 0x%x: 0x%x\n", dev, bm);
158a47f7207Smickey 		}
159a47f7207Smickey #endif
160a47f7207Smickey 		/*
161a47f7207Smickey 		 * If extended disk access functions are not supported
162a47f7207Smickey 		 * there is not much point on doing EDD.
163a47f7207Smickey 		 */
164a47f7207Smickey 		if (!(pdi->bios_edd & EXT_BM_EDA))
165a47f7207Smickey 			pdi->bios_edd = -1;
166a47f7207Smickey 	} else
167a47f7207Smickey 		pdi->bios_edd = -1;
168a47f7207Smickey 
16931235824Sreyk 	/* Skip sanity check for CHS options in EDD mode. */
17031235824Sreyk 	if (pdi->bios_edd != -1)
17131235824Sreyk 		return 0;
17231235824Sreyk 
17331235824Sreyk 	/* Sanity check */
17431235824Sreyk 	if (!pdi->bios_cylinders || !pdi->bios_heads || !pdi->bios_sectors)
175c58c0b92Skrw 		return 1;
17631235824Sreyk 
17731235824Sreyk 	/* CD-ROMs sometimes return heads == 1 */
17831235824Sreyk 	if (pdi->bios_heads < 2)
179c58c0b92Skrw 		return 1;
18031235824Sreyk 
181c58c0b92Skrw 	return 0;
182a47f7207Smickey }
183a47f7207Smickey 
184a47f7207Smickey /*
185a47f7207Smickey  * Read/Write a block from given place using the BIOS.
186a47f7207Smickey  */
1873830afd3Sderaadt int
CHS_rw(int rw,int dev,int cyl,int head,int sect,int nsect,void * buf)188a47f7207Smickey CHS_rw(int rw, int dev, int cyl, int head, int sect, int nsect, void *buf)
189a47f7207Smickey {
190a47f7207Smickey 	int rv;
191a47f7207Smickey 
192a47f7207Smickey 	rw = rw == F_READ ? 2 : 3;
193a47f7207Smickey 	BIOS_regs.biosr_es = (u_int32_t)buf >> 4;
1942df76cc2Sguenther 	__asm volatile ("movb %b7, %h1\n\t"
195a47f7207Smickey 	    "movb %b6, %%dh\n\t"
196a47f7207Smickey 	    "andl $0xf, %4\n\t"
197a47f7207Smickey 	    /* cylinder; the highest 2 bits of cyl is in %cl */
198a47f7207Smickey 	    "xchgb %%ch, %%cl\n\t"
199a47f7207Smickey 	    "rorb  $2, %%cl\n\t"
200a47f7207Smickey 	    "orb %b5, %%cl\n\t"
201a47f7207Smickey 	    "inc %%cx\n\t"
202a47f7207Smickey 	    DOINT(0x13) "\n\t"
203a47f7207Smickey 	    "setc %b0"
204a47f7207Smickey 	    : "=a" (rv)
205a47f7207Smickey 	    : "0" (nsect), "d" (dev), "c" (cyl),
206a47f7207Smickey 	      "b" (buf), "m" (sect), "m" (head),
207a47f7207Smickey 	      "m" (rw)
208a47f7207Smickey 	    : "cc", "memory");
209a47f7207Smickey 
210c58c0b92Skrw 	return ((rv & 0xff)? rv >> 8 : 0);
211a47f7207Smickey }
212a47f7207Smickey 
213a47f7207Smickey static __inline int
EDD_rw(int rw,int dev,u_int32_t daddr,u_int32_t nblk,void * buf)2149730fd37Skrw EDD_rw(int rw, int dev, u_int32_t daddr, u_int32_t nblk, void *buf)
215a47f7207Smickey {
216a47f7207Smickey 	int rv;
217a47f7207Smickey 	volatile static struct EDD_CB cb;
218a47f7207Smickey 
219a47f7207Smickey 	/* Zero out reserved stuff */
220a47f7207Smickey 	cb.edd_res1 = 0;
221a47f7207Smickey 	cb.edd_res2 = 0;
222a47f7207Smickey 
223a47f7207Smickey 	/* Fill in parameters */
224a47f7207Smickey 	cb.edd_len = sizeof(cb);
225a47f7207Smickey 	cb.edd_nblk = nblk;
226a47f7207Smickey 	cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff;
227a47f7207Smickey 	cb.edd_off = (u_int32_t)buf & 0xf;
228a47f7207Smickey 	cb.edd_daddr = daddr;
229a47f7207Smickey 
230a47f7207Smickey 	/* if offset/segment are zero, punt */
231a47f7207Smickey 	if (!cb.edd_seg && !cb.edd_off)
232c58c0b92Skrw 		return 1;
233a47f7207Smickey 
234a47f7207Smickey 	/* Call extended read/write (with disk packet) */
235a47f7207Smickey 	BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4;
2362df76cc2Sguenther 	__asm volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
237a47f7207Smickey 	    : "0" ((rw == F_READ)? 0x4200: 0x4300),
238a47f7207Smickey 	      "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc");
239c58c0b92Skrw 	return ((rv & 0xff)? rv >> 8 : 0);
240a47f7207Smickey }
241a47f7207Smickey 
242a47f7207Smickey /*
243a47f7207Smickey  * Read given sector, handling retry/errors/etc.
244a47f7207Smickey  */
245a47f7207Smickey int
biosd_io(int rw,bios_diskinfo_t * bd,u_int off,int nsect,void * buf)2469730fd37Skrw biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf)
247a47f7207Smickey {
248a47f7207Smickey 	int dev = bd->bios_number;
249a47f7207Smickey 	int j, error;
2507b5f1cbeSyasuoka 	void *bb, *bb1 = NULL;
251338feb3aStom 	int bbsize = nsect * DEV_BSIZE;
252a47f7207Smickey 
253338feb3aStom 	if (bd->flags & BDI_EL_TORITO) {	/* It's a CD device */
254338feb3aStom 		dev &= 0xff;			/* Mask out this flag bit */
255338feb3aStom 
256338feb3aStom 		/*
257338feb3aStom 		 * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors
258338feb3aStom 		 * to DEV_BSIZE blocks before calling the device strategy
259338feb3aStom 		 * routine.  However, the El Torito spec says that the
260338feb3aStom 		 * BIOS will work in 2,048-byte sectors.  So shift back.
261338feb3aStom 		 */
262209ccf4dSderaadt 		off /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
263209ccf4dSderaadt 		nsect /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
264338feb3aStom 	}
265338feb3aStom 
266338feb3aStom 	/*
267338feb3aStom 	 * Use a bounce buffer to not cross 64k DMA boundary, and to
268338feb3aStom 	 * not access 1 MB or above.
269338feb3aStom 	 */
270338feb3aStom 	if (((((u_int32_t)buf) & ~0xffff) !=
271338feb3aStom 	    (((u_int32_t)buf + bbsize) & ~0xffff)) ||
272338feb3aStom 	    (((u_int32_t)buf) >= 0x100000)) {
273be4b99ebSjsing 		bb = bb1 = alloc(bbsize * 2);
274be4b99ebSjsing 		if ((((u_int32_t)bb) & ~0xffff) !=
275be4b99ebSjsing 		    (((u_int32_t)bb + bbsize - 1) & ~0xffff))
276be4b99ebSjsing 			bb = (void *)(((u_int32_t)bb + bbsize - 1) & ~0xffff);
277a47f7207Smickey 		if (rw != F_READ)
278338feb3aStom 			bcopy(buf, bb, bbsize);
279a47f7207Smickey 	} else
280a47f7207Smickey 		bb = buf;
281a47f7207Smickey 
282a47f7207Smickey 	/* Try to do operation up to 5 times */
283a47f7207Smickey 	for (error = 1, j = 5; j-- && error; ) {
284a47f7207Smickey 		/* CHS or LBA access? */
285a47f7207Smickey 		if (bd->bios_edd != -1) {
286a47f7207Smickey 			error = EDD_rw(rw, dev, off, nsect, bb);
287a47f7207Smickey 		} else {
288a47f7207Smickey 			int cyl, head, sect;
289a47f7207Smickey 			size_t i, n;
290a47f7207Smickey 			char *p = bb;
291a47f7207Smickey 
292a47f7207Smickey 			/* Handle track boundaries */
293a47f7207Smickey 			for (error = i = 0; error == 0 && i < nsect;
294a47f7207Smickey 			    i += n, off += n, p += n * DEV_BSIZE) {
295a47f7207Smickey 
296338feb3aStom 				btochs(off, cyl, head, sect, bd->bios_heads,
297338feb3aStom 				    bd->bios_sectors);
298338feb3aStom 
299a47f7207Smickey 				if ((sect + (nsect - i)) >= bd->bios_sectors)
300a47f7207Smickey 					n = bd->bios_sectors - sect;
301a47f7207Smickey 				else
302a47f7207Smickey 					n = nsect - i;
303a47f7207Smickey 
304a47f7207Smickey 				error = CHS_rw(rw, dev, cyl, head, sect, n, p);
305a47f7207Smickey 
306a47f7207Smickey 				/* ECC corrected */
307a47f7207Smickey 				if (error == 0x11)
308a47f7207Smickey 					error = 0;
309a47f7207Smickey 			}
310a47f7207Smickey 		}
311a47f7207Smickey 		switch (error) {
312a47f7207Smickey 		case 0x00:	/* No errors */
313a47f7207Smickey 		case 0x11:	/* ECC corrected */
314a47f7207Smickey 			error = 0;
315a47f7207Smickey 			break;
316a47f7207Smickey 
317a47f7207Smickey 		default:	/* All other errors */
318a47f7207Smickey #ifdef BIOS_DEBUG
319a47f7207Smickey 			if (debug)
320a47f7207Smickey 				printf("\nBIOS error 0x%x (%s)\n",
321a47f7207Smickey 				    error, biosdisk_err(error));
322a47f7207Smickey #endif
323a47f7207Smickey 			biosdreset(dev);
324a47f7207Smickey 			break;
325a47f7207Smickey 		}
326a47f7207Smickey 	}
327a47f7207Smickey 
328a47f7207Smickey 	if (bb != buf && rw == F_READ)
329338feb3aStom 		bcopy(bb, buf, bbsize);
330be4b99ebSjsing 	free(bb1, bbsize * 2);
331a47f7207Smickey 
332a47f7207Smickey #ifdef BIOS_DEBUG
333a47f7207Smickey 	if (debug) {
334a47f7207Smickey 		if (error != 0)
335a47f7207Smickey 			printf("=0x%x(%s)", error, biosdisk_err(error));
336a47f7207Smickey 		putchar('\n');
337a47f7207Smickey 	}
338a47f7207Smickey #endif
339a47f7207Smickey 
340338feb3aStom 	return error;
341a47f7207Smickey }
342a47f7207Smickey 
3436ab058fbSotto #define MAXSECTS 32
3446ab058fbSotto 
3457b5f1cbeSyasuoka int
biosd_diskio(int rw,struct diskinfo * dip,u_int off,int nsect,void * buf)3467b5f1cbeSyasuoka biosd_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
3477b5f1cbeSyasuoka {
3486ab058fbSotto 	char *dest = buf;
3496ab058fbSotto 	int n, ret;
3506ab058fbSotto 
3516ab058fbSotto 	/*
3526ab058fbSotto 	 * Avoid doing too large reads, the bounce buffer used by biosd_io()
3536ab058fbSotto 	 * might run us out-of-mem.
3546ab058fbSotto 	 */
3556ab058fbSotto 	for (ret = 0; ret == 0 && nsect > 0;
3566ab058fbSotto 	    off += MAXSECTS, dest += MAXSECTS * DEV_BSIZE, nsect -= MAXSECTS) {
3576ab058fbSotto 		n = nsect >= MAXSECTS ? MAXSECTS : nsect;
3586ab058fbSotto 		ret = biosd_io(rw, &dip->bios_info, off, n, dest);
3597b5f1cbeSyasuoka 	}
3606ab058fbSotto 	return ret;
3616ab058fbSotto }
3626ab058fbSotto 
363a47f7207Smickey /*
364e995780eSjsing  * Try to read the bsd label on the given BIOS device.
365a47f7207Smickey  */
3669730fd37Skrw static u_int
findopenbsd(bios_diskinfo_t * bd,const char ** err)367fb9d9d2cSkrw findopenbsd(bios_diskinfo_t *bd, const char **err)
368d173b221Sreyk {
369d173b221Sreyk 	struct dos_mbr mbr;
370d173b221Sreyk 	struct dos_partition *dp;
371fb9d9d2cSkrw 	u_int mbroff = DOSBBSECTOR;
372fb9d9d2cSkrw 	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
373fb9d9d2cSkrw 	int error, i, maxebr = DOS_MAXEBR, nextebr;
374d173b221Sreyk 
375fb9d9d2cSkrw again:
376fb9d9d2cSkrw 	if (!maxebr--) {
377d173b221Sreyk 		*err = "too many extended partitions";
378df8d875bSderaadt 		return (-1);
379d173b221Sreyk 	}
380d173b221Sreyk 
381d173b221Sreyk 	/* Read MBR */
3829b016634Skrw 	bzero(&mbr, sizeof(mbr));
383df8d875bSderaadt 	error = biosd_io(F_READ, bd, mbroff, 1, &mbr);
384d173b221Sreyk 	if (error) {
385d173b221Sreyk 		*err = biosdisk_err(error);
386df8d875bSderaadt 		return (-1);
387d173b221Sreyk 	}
388d173b221Sreyk 
389d173b221Sreyk 	/* check mbr signature */
390d173b221Sreyk 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
391d173b221Sreyk 		*err = "bad MBR signature\n";
392df8d875bSderaadt 		return (-1);
393d173b221Sreyk 	}
394d173b221Sreyk 
395d173b221Sreyk 	/* Search for OpenBSD partition */
396fb9d9d2cSkrw 	nextebr = 0;
397aac5f9eeSreyk 	for (i = 0; i < NDOSPART; i++) {
398d173b221Sreyk 		dp = &mbr.dmbr_parts[i];
399df8d875bSderaadt 		if (!dp->dp_size)
400df8d875bSderaadt 			continue;
401d173b221Sreyk #ifdef BIOS_DEBUG
402d173b221Sreyk 		if (debug)
403d173b221Sreyk 			printf("found partition %u: "
404d173b221Sreyk 			    "type %u (0x%x) offset %u (0x%x)\n",
405d173b221Sreyk 			    (int)(dp - mbr.dmbr_parts),
406d173b221Sreyk 			    dp->dp_typ, dp->dp_typ,
407d173b221Sreyk 			    dp->dp_start, dp->dp_start);
408d173b221Sreyk #endif
409aac5f9eeSreyk 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
4109730fd37Skrw 			if (dp->dp_start > (dp->dp_start + mbroff))
4119730fd37Skrw 				continue;
412fb9d9d2cSkrw 			return (dp->dp_start + mbroff);
4138255a185Skrw 		}
4148255a185Skrw 
415fb9d9d2cSkrw 		/*
416fb9d9d2cSkrw 		 * Record location of next ebr if and only if this is the first
417fb9d9d2cSkrw 		 * extended partition in this boot record!
418fb9d9d2cSkrw 		 */
419fb9d9d2cSkrw 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
420fb9d9d2cSkrw 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
421fb9d9d2cSkrw 			nextebr = dp->dp_start + mbr_eoff;
422fb9d9d2cSkrw 			if (nextebr < dp->dp_start)
423fb9d9d2cSkrw 				nextebr = (u_int)-1;
4249730fd37Skrw 			if (mbr_eoff == DOSBBSECTOR)
4259730fd37Skrw 				mbr_eoff = dp->dp_start;
426d173b221Sreyk 		}
427df8d875bSderaadt 	}
428df8d875bSderaadt 
429fb9d9d2cSkrw 	if (nextebr && nextebr != (u_int)-1) {
430fb9d9d2cSkrw 		mbroff = nextebr;
431fb9d9d2cSkrw 		goto again;
432fb9d9d2cSkrw 	}
433fb9d9d2cSkrw 
434fb9d9d2cSkrw 	return (-1);
435df8d875bSderaadt }
436d173b221Sreyk 
437a47f7207Smickey const char *
bios_getdisklabel(bios_diskinfo_t * bd,struct disklabel * label)438a47f7207Smickey bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
439a47f7207Smickey {
4409730fd37Skrw 	u_int start = 0;
4417b5f1cbeSyasuoka 	char buf[DEV_BSIZE];
442d173b221Sreyk 	const char *err = NULL;
443d173b221Sreyk 	int error;
444a47f7207Smickey 
445a47f7207Smickey 	/* Sanity check */
44631235824Sreyk 	if (bd->bios_edd == -1 &&
44731235824Sreyk 	    (bd->bios_heads == 0 || bd->bios_sectors == 0))
448c58c0b92Skrw 		return "failed to read disklabel";
449a47f7207Smickey 
450a47f7207Smickey 	/* MBR is a harddisk thing */
451a47f7207Smickey 	if (bd->bios_number & 0x80) {
452fb9d9d2cSkrw 		start = findopenbsd(bd, &err);
4539730fd37Skrw 		if (start == (u_int)-1) {
454d173b221Sreyk 			if (err != NULL)
455d173b221Sreyk 				return (err);
456d173b221Sreyk 			return "no OpenBSD partition\n";
457d173b221Sreyk 		}
458d173b221Sreyk 	}
459a47f7207Smickey 
460a47f7207Smickey 	/* Load BSD disklabel */
461a47f7207Smickey #ifdef BIOS_DEBUG
462a47f7207Smickey 	if (debug)
4639e746c39Skrw 		printf("loading disklabel @ %u\n", start + DOS_LABELSECTOR);
464a47f7207Smickey #endif
465a47f7207Smickey 	/* read disklabel */
4669e746c39Skrw 	error = biosd_io(F_READ, bd, start + DOS_LABELSECTOR, 1, buf);
467a47f7207Smickey 
468a47f7207Smickey 	if (error)
469c58c0b92Skrw 		return "failed to read disklabel";
470a47f7207Smickey 
471a47f7207Smickey 	/* Fill in disklabel */
472a47f7207Smickey 	return (getdisklabel(buf, label));
473a47f7207Smickey }
474a47f7207Smickey 
475a47f7207Smickey int
biosopen(struct open_file * f,...)476a47f7207Smickey biosopen(struct open_file *f, ...)
477a47f7207Smickey {
47892befcddSjsing #ifdef SOFTRAID
479e995780eSjsing 	struct sr_boot_volume *bv;
48092befcddSjsing #endif
481a47f7207Smickey 	register char *cp, **file;
482a47f7207Smickey 	dev_t maj, unit, part;
483a47f7207Smickey 	struct diskinfo *dip;
484e995780eSjsing 	int biosdev, devlen;
485e995780eSjsing 	const char *st;
486e995780eSjsing 	va_list ap;
487e995780eSjsing 	char *dev;
488a47f7207Smickey 
489a47f7207Smickey 	va_start(ap, f);
490a47f7207Smickey 	cp = *(file = va_arg(ap, char **));
491a47f7207Smickey 	va_end(ap);
492a47f7207Smickey 
493a47f7207Smickey #ifdef BIOS_DEBUG
494a47f7207Smickey 	if (debug)
495a47f7207Smickey 		printf("%s\n", cp);
496a47f7207Smickey #endif
497a47f7207Smickey 
498a47f7207Smickey 	f->f_devdata = NULL;
499e995780eSjsing 
500e995780eSjsing 	/* Search for device specification. */
501e995780eSjsing 	dev = cp;
502e995780eSjsing 	if (cp[4] == ':')
503e995780eSjsing 		devlen = 2;
504e995780eSjsing 	else if (cp[5] == ':')
505e995780eSjsing 		devlen = 3;
506a47f7207Smickey 	else
507e995780eSjsing 		return ENOENT;
508e995780eSjsing 	cp += devlen;
509e995780eSjsing 
510e995780eSjsing 	/* Get unit. */
511e995780eSjsing 	if ('0' <= *cp && *cp <= '9')
512e995780eSjsing 		unit = *cp++ - '0';
513e995780eSjsing 	else {
514e995780eSjsing 		printf("Bad unit number\n");
515e995780eSjsing 		return EUNIT;
516a47f7207Smickey 	}
517a47f7207Smickey 
518e995780eSjsing 	/* Get partition. */
519e995780eSjsing 	if ('a' <= *cp && *cp <= 'p')
520e995780eSjsing 		part = *cp++ - 'a';
521e995780eSjsing 	else {
522e995780eSjsing 		printf("Bad partition\n");
523e995780eSjsing 		return EPART;
524e995780eSjsing 	}
525e995780eSjsing 
526e995780eSjsing 	/* Get filename. */
527e995780eSjsing 	cp++;	/* skip ':' */
528e995780eSjsing 	if (*cp != 0)
529e995780eSjsing 		*file = cp;
530e995780eSjsing 	else
531e995780eSjsing 		f->f_flags |= F_RAW;
532e995780eSjsing 
53392befcddSjsing #ifdef SOFTRAID
534e995780eSjsing 	/* Intercept softraid disks. */
535e995780eSjsing 	if (strncmp("sr", dev, 2) == 0) {
5365a837827Skn 		/* We only support read-only softraid. */
5375a837827Skn 		f->f_flags |= F_NOWRITE;
538e995780eSjsing 
539e995780eSjsing 		/* Create a fake diskinfo for this softraid volume. */
540e995780eSjsing 		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
541e995780eSjsing 			if (bv->sbv_unit == unit)
542e995780eSjsing 				break;
543e995780eSjsing 		if (bv == NULL) {
544e995780eSjsing 			printf("Unknown device: sr%d\n", unit);
545e995780eSjsing 			return EADAPT;
546e995780eSjsing 		}
54770197c51Sjsing 
548855f8c03Sstsp 		if ((bv->sbv_level == 'C' || bv->sbv_level == 0x1C) &&
549855f8c03Sstsp 		    bv->sbv_keys == NULL) {
55027bea9a3Sjsing 			if (sr_crypto_unlock_volume(bv) != 0)
551a8401cd3Sjsing 				return EPERM;
552855f8c03Sstsp 		}
55370197c51Sjsing 
554e995780eSjsing 		if (bv->sbv_diskinfo == NULL) {
555e995780eSjsing 			dip = alloc(sizeof(struct diskinfo));
556e995780eSjsing 			bzero(dip, sizeof(*dip));
5576ce082ffSyasuoka 			dip->strategy = biosstrategy;
558e995780eSjsing 			bv->sbv_diskinfo = dip;
559e995780eSjsing 			dip->sr_vol = bv;
560e995780eSjsing 			dip->bios_info.flags |= BDI_BADLABEL;
561e995780eSjsing 		}
562e995780eSjsing 
563e995780eSjsing 		dip = bv->sbv_diskinfo;
564e995780eSjsing 
565e995780eSjsing 		if (dip->bios_info.flags & BDI_BADLABEL) {
566e995780eSjsing 			/* Attempt to read disklabel. */
567e995780eSjsing 			bv->sbv_part = 'c';
568e995780eSjsing 			if (sr_getdisklabel(bv, &dip->disklabel))
569e995780eSjsing 				return ERDLAB;
570e995780eSjsing 			dip->bios_info.flags &= ~BDI_BADLABEL;
5716ce082ffSyasuoka 			check_hibernate(dip);
572e995780eSjsing 		}
573e995780eSjsing 
574e995780eSjsing 		bv->sbv_part = part + 'a';
575e995780eSjsing 
576e995780eSjsing 		bootdev_dip = dip;
577e995780eSjsing 		f->f_devdata = dip;
578e995780eSjsing 
579e995780eSjsing 		return 0;
580e995780eSjsing 	}
58192befcddSjsing #endif
582e995780eSjsing 
583e995780eSjsing 	for (maj = 0; maj < nbdevs &&
584e995780eSjsing 	    strncmp(dev, bdevs[maj], devlen); maj++);
585a47f7207Smickey 	if (maj >= nbdevs) {
586a47f7207Smickey 		printf("Unknown device: ");
587a47f7207Smickey 		for (cp = *file; *cp != ':'; cp++)
588a47f7207Smickey 			putchar(*cp);
589a47f7207Smickey 		putchar('\n');
590a47f7207Smickey 		return EADAPT;
591a47f7207Smickey 	}
592a47f7207Smickey 
593a47f7207Smickey 	biosdev = unit;
594a47f7207Smickey 	switch (maj) {
595a47f7207Smickey 	case 0:  /* wd */
596a47f7207Smickey 	case 4:  /* sd */
597a47f7207Smickey 	case 17: /* hd */
598a47f7207Smickey 		biosdev |= 0x80;
599a47f7207Smickey 		break;
600a47f7207Smickey 	case 2:  /* fd */
601a47f7207Smickey 		break;
602338feb3aStom 	case 6:  /* cd */
603338feb3aStom 		biosdev = bios_bootdev & 0xff;
604338feb3aStom 		break;
605a47f7207Smickey 	default:
606a47f7207Smickey 		return ENXIO;
607a47f7207Smickey 	}
608a47f7207Smickey 
609a47f7207Smickey 	/* Find device */
610a47f7207Smickey 	bootdev_dip = dip = dklookup(biosdev);
611a47f7207Smickey 
612a47f7207Smickey 	/* Fix up bootdev */
613a47f7207Smickey 	{ dev_t bsd_dev;
614a47f7207Smickey 		bsd_dev = dip->bios_info.bsd_dev;
615a47f7207Smickey 		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
616a47f7207Smickey 		    B_CONTROLLER(bsd_dev), unit, part);
617a47f7207Smickey 		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
618a47f7207Smickey 		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
619a47f7207Smickey 	}
620a47f7207Smickey 
621a47f7207Smickey #if 0
622a47f7207Smickey 	dip->bios_info.bsd_dev = dip->bootdev;
623a47f7207Smickey 	bootdev = dip->bootdev;
624a47f7207Smickey #endif
625a47f7207Smickey 
626a47f7207Smickey #ifdef BIOS_DEBUG
627a47f7207Smickey 	if (debug) {
628a47f7207Smickey 		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
629a47f7207Smickey 		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
630a47f7207Smickey 		    dip->bios_info.bios_edd);
631a47f7207Smickey 	}
632a47f7207Smickey #endif
633a47f7207Smickey 
634a47f7207Smickey 	/* Try for disklabel again (might be removable media) */
635a47f7207Smickey 	if (dip->bios_info.flags & BDI_BADLABEL) {
636e995780eSjsing 		st = bios_getdisklabel(&dip->bios_info, &dip->disklabel);
637a47f7207Smickey #ifdef BIOS_DEBUG
638a47f7207Smickey 		if (debug && st)
639a47f7207Smickey 			printf("%s\n", st);
640a47f7207Smickey #endif
641a47f7207Smickey 		if (!st) {
642a47f7207Smickey 			dip->bios_info.flags &= ~BDI_BADLABEL;
643a47f7207Smickey 			dip->bios_info.flags |= BDI_GOODLABEL;
644a47f7207Smickey 		} else
645c58c0b92Skrw 			return ERDLAB;
646a47f7207Smickey 	}
647a47f7207Smickey 
648a47f7207Smickey 	f->f_devdata = dip;
649a47f7207Smickey 
650a47f7207Smickey 	return 0;
651a47f7207Smickey }
652a47f7207Smickey 
653a47f7207Smickey const u_char bidos_errs[] =
654a47f7207Smickey /* ignored	"\x00" "successful completion\0" */
655a47f7207Smickey 		"\x01" "invalid function/parameter\0"
656a47f7207Smickey 		"\x02" "address mark not found\0"
657a47f7207Smickey 		"\x03" "write-protected\0"
658a47f7207Smickey 		"\x04" "sector not found\0"
659a47f7207Smickey 		"\x05" "reset failed\0"
660a47f7207Smickey 		"\x06" "disk changed\0"
661a47f7207Smickey 		"\x07" "drive parameter activity failed\0"
662a47f7207Smickey 		"\x08" "DMA overrun\0"
663a47f7207Smickey 		"\x09" "data boundary error\0"
664a47f7207Smickey 		"\x0A" "bad sector detected\0"
665a47f7207Smickey 		"\x0B" "bad track detected\0"
666a47f7207Smickey 		"\x0C" "invalid media\0"
667a47f7207Smickey 		"\x0E" "control data address mark detected\0"
668a47f7207Smickey 		"\x0F" "DMA arbitration level out of range\0"
669a47f7207Smickey 		"\x10" "uncorrectable CRC or ECC error on read\0"
670a47f7207Smickey /* ignored	"\x11" "data ECC corrected\0" */
671a47f7207Smickey 		"\x20" "controller failure\0"
672a47f7207Smickey 		"\x31" "no media in drive\0"
673a47f7207Smickey 		"\x32" "incorrect drive type in CMOS\0"
674a47f7207Smickey 		"\x40" "seek failed\0"
675a47f7207Smickey 		"\x80" "operation timed out\0"
676a47f7207Smickey 		"\xAA" "drive not ready\0"
677a47f7207Smickey 		"\xB0" "volume not locked in drive\0"
678a47f7207Smickey 		"\xB1" "volume locked in drive\0"
679a47f7207Smickey 		"\xB2" "volume not removable\0"
680a47f7207Smickey 		"\xB3" "volume in use\0"
681a47f7207Smickey 		"\xB4" "lock count exceeded\0"
682a47f7207Smickey 		"\xB5" "valid eject request failed\0"
683a47f7207Smickey 		"\xBB" "undefined error\0"
684a47f7207Smickey 		"\xCC" "write fault\0"
685a47f7207Smickey 		"\xE0" "status register error\0"
686a47f7207Smickey 		"\xFF" "sense operation failed\0"
687a47f7207Smickey 		"\x00" "\0";
688a47f7207Smickey 
689a47f7207Smickey static const char *
biosdisk_err(u_int error)690a47f7207Smickey biosdisk_err(u_int error)
691a47f7207Smickey {
692a47f7207Smickey 	register const u_char *p = bidos_errs;
693a47f7207Smickey 
694a47f7207Smickey 	while (*p && *p != error)
695a47f7207Smickey 		while (*p++);
696a47f7207Smickey 
697a47f7207Smickey 	return ++p;
698a47f7207Smickey }
699a47f7207Smickey 
700a47f7207Smickey const struct biosdisk_errors {
701a47f7207Smickey 	u_char error;
702a47f7207Smickey 	u_char errno;
703a47f7207Smickey } tab[] = {
704a47f7207Smickey 	{ 0x01, EINVAL },
705a47f7207Smickey 	{ 0x03, EROFS },
706a47f7207Smickey 	{ 0x08, EINVAL },
707a47f7207Smickey 	{ 0x09, EINVAL },
708a47f7207Smickey 	{ 0x0A, EBSE },
709a47f7207Smickey 	{ 0x0B, EBSE },
710a47f7207Smickey 	{ 0x0C, ENXIO },
711a47f7207Smickey 	{ 0x0D, EINVAL },
712a47f7207Smickey 	{ 0x10, EECC },
713a47f7207Smickey 	{ 0x20, EHER },
714a47f7207Smickey 	{ 0x31, ENXIO },
715a47f7207Smickey 	{ 0x32, ENXIO },
716a47f7207Smickey 	{ 0x00, EIO }
717a47f7207Smickey };
718a47f7207Smickey 
719a47f7207Smickey static int
biosdisk_errno(u_int error)720a47f7207Smickey biosdisk_errno(u_int error)
721a47f7207Smickey {
722a47f7207Smickey 	register const struct biosdisk_errors *p;
723a47f7207Smickey 
724c58c0b92Skrw 	if (error == 0)
725a47f7207Smickey 		return 0;
726a47f7207Smickey 
727*36dba039Sjsg 	for (p = tab; p->error && p->error != error; p++)
728*36dba039Sjsg 		;
729a47f7207Smickey 
730a47f7207Smickey 	return p->errno;
731a47f7207Smickey }
732a47f7207Smickey 
733a47f7207Smickey int
biosstrategy(void * devdata,int rw,daddr_t blk,size_t size,void * buf,size_t * rsize)7343e58d19eSkrw biosstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
735a47f7207Smickey     size_t *rsize)
736a47f7207Smickey {
737a47f7207Smickey 	struct diskinfo *dip = (struct diskinfo *)devdata;
738a47f7207Smickey 	u_int8_t error = 0;
739a47f7207Smickey 	size_t nsect;
740a47f7207Smickey 
74192befcddSjsing #ifdef SOFTRAID
742e995780eSjsing 	/* Intercept strategy for softraid volumes. */
743e995780eSjsing 	if (dip->sr_vol)
744e995780eSjsing 		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
74592befcddSjsing #endif
746e995780eSjsing 
747a47f7207Smickey 	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
748e995780eSjsing 	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
749a47f7207Smickey 
750a47f7207Smickey 	/* Read all, sub-functions handle track boundaries */
7519730fd37Skrw 	if (blk < 0)
7529730fd37Skrw 		error = EINVAL;
7539730fd37Skrw 	else
7546ab058fbSotto 		error = biosd_diskio(rw, dip, blk, nsect, buf);
755a47f7207Smickey 
756a47f7207Smickey #ifdef BIOS_DEBUG
757a47f7207Smickey 	if (debug) {
758a47f7207Smickey 		if (error != 0)
759a47f7207Smickey 			printf("=0x%x(%s)", error, biosdisk_err(error));
760a47f7207Smickey 		putchar('\n');
761a47f7207Smickey 	}
762a47f7207Smickey #endif
763a47f7207Smickey 
764a47f7207Smickey 	if (rsize != NULL)
765a47f7207Smickey 		*rsize = nsect * DEV_BSIZE;
766a47f7207Smickey 
767c58c0b92Skrw 	return (biosdisk_errno(error));
768a47f7207Smickey }
769a47f7207Smickey 
770a47f7207Smickey int
biosclose(struct open_file * f)771a47f7207Smickey biosclose(struct open_file *f)
772a47f7207Smickey {
773a47f7207Smickey 	f->f_devdata = NULL;
774c58c0b92Skrw 
775a47f7207Smickey 	return 0;
776a47f7207Smickey }
777a47f7207Smickey 
778a47f7207Smickey int
biosioctl(struct open_file * f,u_long cmd,void * data)779a47f7207Smickey biosioctl(struct open_file *f, u_long cmd, void *data)
780a47f7207Smickey {
781a47f7207Smickey 	return 0;
782a47f7207Smickey }
783