1/*	$NetBSD: biosdisk.c,v 1.4 2021/05/17 19:31:38 mrg Exp $	*/
2
3/*
4 * Copyright (c) 1996, 1998
5 *	Matthias Drochner.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29/*
30 * raw BIOS disk device for libsa.
31 * needs lowlevel parts from bios_disk.S and biosdisk_ll.c
32 * partly from netbsd:sys/arch/i386/boot/disk.c
33 * no bad144 handling!
34 *
35 * A lot of this must match sys/kern/subr_disk_mbr.c
36 */
37
38/*
39 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
40 *
41 * Mach Operating System
42 * Copyright (c) 1992, 1991 Carnegie Mellon University
43 * All Rights Reserved.
44 *
45 * Permission to use, copy, modify and distribute this software and its
46 * documentation is hereby granted, provided that both the copyright
47 * notice and this permission notice appear in all copies of the
48 * software, derivative works or modified versions, and any portions
49 * thereof, and that both notices appear in supporting documentation.
50 *
51 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
52 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
53 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
54 *
55 * Carnegie Mellon requests users of this software to return to
56 *
57 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
58 *  School of Computer Science
59 *  Carnegie Mellon University
60 *  Pittsburgh PA 15213-3890
61 *
62 * any improvements or extensions that they make and grant Carnegie Mellon
63 * the rights to redistribute these changes.
64 */
65
66#include <sys/types.h>
67#include <sys/disklabel.h>
68
69#include <lib/libkern/libkern.h>
70#include <lib/libsa/stand.h>
71#include <lib/libsa/saerrno.h>
72#include <lib/libsa/loadfile.h>
73
74#include <machine/disklabel.h>
75
76#include "biosdisk.h"
77#include "biosdisk_ll.h"
78
79#include "bootinfo.h"
80
81#define BUFSIZE (1 * BIOSDISK_SECSIZE)
82
83struct biosdisk {
84	int	dev;
85	int	boff;
86	char	buf[BUFSIZE];
87};
88
89static struct btinfo_bootdisk bi_disk;
90
91#define	RF_PROTECTED_SECTORS	64	/* XXX refer to <.../rf_optnames.h> */
92
93static struct biosdisk *
94alloc_biosdisk(int dev)
95{
96	struct biosdisk *d;
97
98	d = (struct biosdisk *)ALLOC(sizeof(*d));
99	if (d == NULL)
100		return (NULL);
101
102	memset(d, 0, sizeof(*d));
103	d->dev = dev;
104
105	return (d);
106}
107
108static int
109check_label(struct biosdisk *d, int sector)
110{
111	struct disklabel *lp;
112
113	/* find partition in NetBSD disklabel */
114	if (readsects(d->dev, sector + LABELSECTOR, d->buf, 1)) {
115#ifdef DISK_DEBUG
116		printf("Error reading disklabel\n");
117#endif
118		return (EIO);
119	}
120
121	lp = (struct disklabel *)(d->buf + LABELOFFSET);
122	if (lp->d_magic != DISKMAGIC || dkcksum(lp)) {
123#ifdef DISK_DEBUG
124		printf("warning: no disklabel\n");
125#endif
126		return (-1);
127	}
128
129	d->boff = sector;
130	return (0);
131}
132
133static int
134read_label(struct biosdisk *d)
135{
136	struct mbr_sector *mbr_sect;
137	struct disklabel dflt_lbl;
138	struct mbr_partition mbr[MBR_PART_COUNT];
139	struct partition *p;
140	int sector, i;
141	int error;
142	int typ;
143	int ext_base, this_ext, next_ext;
144#ifdef COMPAT_386BSD_MBRPART
145	int sector_386bsd = -1;
146#endif
147
148	memset(&dflt_lbl, 0, sizeof dflt_lbl);
149	dflt_lbl.d_npartitions = 8;
150
151	d->boff = 0;
152
153	/*
154	 * find NetBSD Partition in DOS partition table
155	 * XXX check magic???
156	 */
157	ext_base = 0;
158	next_ext = 0;
159	for (;;) {
160		this_ext = ext_base + next_ext;
161		next_ext = 0;
162		mbr_sect = (struct mbr_sector *)d->buf;
163		memcpy(&mbr, mbr_sect->mbr_parts, sizeof(mbr));
164		if (readsects(d->dev, this_ext, d->buf, 1)) {
165#ifdef DISK_DEBUG
166			printf("error reading MBR sector %d\n", this_ext);
167#endif
168			return (EIO);
169		}
170		memcpy(&mbr, mbr_sect->mbr_parts, sizeof(mbr));
171		/* Look for NetBSD partition ID */
172		for (i = 0; i < MBR_PART_COUNT; i++) {
173			typ = mbr[i].mbrp_type;
174			if (typ == 0)
175				continue;
176			sector = this_ext + mbr[i].mbrp_start;
177			if (typ == MBR_PTYPE_NETBSD) {
178				error = check_label(d, sector);
179				if (error >= 0)
180					return error;
181			}
182			if (MBR_IS_EXTENDED(typ)) {
183				next_ext = mbr[i].mbrp_start;
184				continue;
185			}
186#ifdef COMPAT_386BSD_MBRPART
187			if (this_ext == 0 && typ == MBR_PTYPE_386BSD)
188				sector_386bsd = sector;
189#endif
190			if (this_ext != 0) {
191				if (dflt_lbl.d_npartitions >= MAXPARTITIONS)
192					continue;
193				p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++];
194			} else
195				p = &dflt_lbl.d_partitions[i];
196			p->p_offset = sector;
197			p->p_size = mbr[i].mbrp_size;
198			p->p_fstype = xlat_mbr_fstype(typ);
199		}
200		if (next_ext == 0)
201			break;
202		if (ext_base == 0) {
203			ext_base = next_ext;
204			next_ext = 0;
205		}
206	}
207
208	sector = 0;
209#ifdef COMPAT_386BSD_MBRPART
210	if (sector_386bsd != -1) {
211		printf("old BSD partition ID!\n");
212		sector = sector_386bsd;
213	}
214#endif
215
216	/*
217	 * One of two things:
218	 * 	1. no MBR
219	 *	2. no NetBSD partition in MBR
220	 *
221	 * We simply default to "start of disk" in this case and
222	 * press on.
223	 */
224	error = check_label(d, sector);
225	if (error >= 0)
226		return (error);
227
228	/*
229	 * Nothing at start of disk, return info from mbr partitions.
230	 */
231	/* XXX fill it to make checksum match kernel one */
232	dflt_lbl.d_checksum = dkcksum(&dflt_lbl);
233	memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl));
234	return (-1);
235}
236
237
238/*
239 * Determine likely partition for possible sector number of dos
240 * partition.
241 */
242u_int
243biosdisk_findptn(int biosdev, u_int sector)
244{
245	struct biosdisk *d;
246	u_int partition = 0;
247	struct disklabel *lp;
248
249	/* Look for netbsd partition that is the dos boot one */
250	d = alloc_biosdisk(biosdev);
251	if (d == NULL)
252		return (0);
253
254	if (read_label(d) == 0) {
255		lp = (struct disklabel *)(d->buf + LABELOFFSET);
256		for (partition = lp->d_npartitions; --partition;){
257			if (lp->d_partitions[partition].p_fstype == FS_UNUSED)
258				continue;
259			if (lp->d_partitions[partition].p_offset == sector)
260				break;
261		}
262	}
263
264	DEALLOC(d, sizeof(*d));
265	return (partition);
266}
267
268int
269biosdisk_open(struct open_file *f, ...)
270{
271	va_list ap;
272	struct biosdisk *d;
273	struct disklabel *lp;
274	int dev;
275	int partition;
276	int error = 0;
277
278	va_start(ap, f);
279	dev = va_arg(ap, int);
280	d = alloc_biosdisk(dev);
281	if (d == NULL) {
282		error = ENXIO;
283		goto out;
284	}
285
286	partition = va_arg(ap, int);
287	bi_disk.biosdev = d->dev;
288	bi_disk.partition = partition;
289	bi_disk.labelsector = -1;
290
291	if (partition == RAW_PART)
292		goto nolabel;
293	error = read_label(d);
294	if (error == -1) {
295		error = 0;
296		goto nolabel;
297	}
298	if (error)
299		goto out;
300
301	lp = (struct disklabel *)(d->buf + LABELOFFSET);
302	if (partition >= lp->d_npartitions ||
303	   lp->d_partitions[partition].p_fstype == FS_UNUSED) {
304#ifdef DISK_DEBUG
305		printf("illegal partition\n");
306#endif
307		error = EPART;
308		goto out;
309	}
310	bi_disk.labelsector = d->boff + LABELSECTOR;
311	bi_disk.label.type = lp->d_type;
312	memcpy(bi_disk.label.packname, lp->d_packname, 16);
313	bi_disk.label.checksum = lp->d_checksum;
314
315	d->boff = lp->d_partitions[partition].p_offset;
316	if (lp->d_partitions[partition].p_fstype == FS_RAID) {
317		d->boff += RF_PROTECTED_SECTORS;
318	}
319nolabel:
320
321#ifdef DISK_DEBUG
322	printf("partition @%d\n", d->boff);
323#endif
324
325	BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk));
326
327	f->f_devdata = d;
328out:
329        va_end(ap);
330	if (error)
331		DEALLOC(d, sizeof(struct biosdisk));
332	return (error);
333}
334
335int
336biosdisk_close(struct open_file *f)
337{
338	struct biosdisk *d = f->f_devdata;
339
340	DEALLOC(d, sizeof(struct biosdisk));
341	f->f_devdata = NULL;
342
343	return (0);
344}
345
346int
347biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg)
348{
349
350	return (EIO);
351}
352
353int
354biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize)
355{
356	struct biosdisk *d;
357	int blks;
358	int frag;
359
360	if (flag != F_READ)
361		return EROFS;
362
363	d = (struct biosdisk *)devdata;
364
365	dblk += d->boff;
366
367	blks = size / BIOSDISK_SECSIZE;
368	if (blks && readsects(d->dev, dblk, buf, blks)) {
369		if (rsize)
370			*rsize = 0;
371		return (EIO);
372	}
373
374	/* do we really need this? */
375	frag = size % BIOSDISK_SECSIZE;
376	if (frag) {
377		if (readsects(d->dev, dblk + blks, d->buf, 1)) {
378			if (rsize)
379				*rsize = blks * BIOSDISK_SECSIZE;
380			return (EIO);
381		}
382		memcpy(buf + blks * BIOSDISK_SECSIZE, d->buf, frag);
383	}
384
385	if (rsize)
386		*rsize = size;
387	return (0);
388}
389