1 /* $NetBSD: biosdisk.c,v 1.44 2015/01/18 20:18:07 jakllsch Exp $ */
4 * Copyright (c) 1996, 1998
5 * Matthias Drochner. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
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
35 * A lot of this must match sys/kern/subr_disk_mbr.c
39 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
41 * Mach Operating System
42 * Copyright (c) 1992, 1991 Carnegie Mellon University
43 * All Rights Reserved.
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.
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.
55 * Carnegie Mellon requests users of this software to return to
57 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
58 * School of Computer Science
59 * Carnegie Mellon University
60 * Pittsburgh PA 15213-3890
62 * any improvements or extensions that they make and grant Carnegie Mellon
63 * the rights to redistribute these changes.
66 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
70 #include <lib/libkern/libkern.h>
71 #include <lib/libsa/stand.h>
73 #include <sys/types.h>
75 #include <sys/param.h>
76 #include <sys/disklabel.h>
77 #include <sys/disklabel_gpt.h>
80 #include <fs/cd9660/iso.h>
82 #include <lib/libsa/saerrno.h>
83 #include <machine/cpu.h>
86 #include "biosdisk_ll.h"
92 #define BUFSIZE 2048 /* must be large enough for a CD sector */
94 #define BIOSDISKNPART 26
97 struct biosdisk_ll ll
;
100 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
105 } part
[BIOSDISKNPART
];
110 const struct uuid GET_nbsd_raid
= GPT_ENT_TYPE_NETBSD_RAIDFRAME
;
111 const struct uuid GET_nbsd_ffs
= GPT_ENT_TYPE_NETBSD_FFS
;
112 const struct uuid GET_nbsd_lfs
= GPT_ENT_TYPE_NETBSD_LFS
;
113 const struct uuid GET_nbsd_swap
= GPT_ENT_TYPE_NETBSD_SWAP
;
117 static struct btinfo_bootdisk bi_disk
;
118 static struct btinfo_bootwedge bi_wedge
;
121 #define MBR_PARTS(buf) ((char *)(buf) + offsetof(struct mbr_sector, mbr_parts))
123 #define RF_PROTECTED_SECTORS 64 /* XXX refer to <.../rf_optnames.h> */
126 biosdisk_strategy(void *devdata
, int flag
, daddr_t dblk
, size_t size
,
127 void *buf
, size_t *rsize
)
135 d
= (struct biosdisk
*) devdata
;
137 if (d
->ll
.type
== BIOSDISK_TYPE_CD
)
138 dblk
= dblk
* DEV_BSIZE
/ ISO_DEFAULT_BLOCK_SIZE
;
142 blks
= size
/ d
->ll
.secsize
;
143 if (blks
&& readsects(&d
->ll
, dblk
, blks
, buf
, 0)) {
150 frag
= size
% d
->ll
.secsize
;
152 if (readsects(&d
->ll
, dblk
+ blks
, 1, d
->buf
, 0)) {
154 *rsize
= blks
* d
->ll
.secsize
;
157 memcpy(buf
+ blks
* d
->ll
.secsize
, d
->buf
, frag
);
165 static struct biosdisk
*
166 alloc_biosdisk(int biosdev
)
170 d
= alloc(sizeof(*d
));
173 memset(d
, 0, sizeof(*d
));
176 if (set_geometry(&d
->ll
, NULL
)) {
178 printf("no geometry information\n");
180 dealloc(d
, sizeof(*d
));
186 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
188 md5(void *hash
, const void *data
, size_t len
)
193 MD5Update(&ctx
, data
, len
);
194 MD5Final(hash
, &ctx
);
202 guid_is_nil(const struct uuid
*u
)
204 static const struct uuid nil
= { .time_low
= 0 };
205 return (memcmp(u
, &nil
, sizeof(*u
)) == 0 ? true : false);
209 guid_is_equal(const struct uuid
*a
, const struct uuid
*b
)
211 return (memcmp(a
, b
, sizeof(*a
)) == 0 ? true : false);
215 check_gpt(struct biosdisk
*d
, daddr_t sector
)
218 const struct gpt_ent
*ep
;
219 const struct uuid
*u
;
228 /* read in gpt_hdr sector */
229 if (readsects(&d
->ll
, sector
, 1, d
->buf
, 1)) {
231 printf("Error reading GPT header at %"PRId64
"\n", sector
);
236 memcpy(&gpth
, d
->buf
, sizeof(gpth
));
238 if (memcmp(GPT_HDR_SIG
, gpth
.hdr_sig
, sizeof(gpth
.hdr_sig
)))
241 crc
= gpth
.hdr_crc_self
;
242 gpth
.hdr_crc_self
= 0;
243 gpth
.hdr_crc_self
= crc32(0, (const void *)&gpth
, GPT_HDR_SIZE
);
244 if (gpth
.hdr_crc_self
!= crc
) {
248 if (gpth
.hdr_lba_self
!= sector
)
252 bi_wedge
.matchblk
= sector
;
253 bi_wedge
.matchnblks
= 1;
255 md5(bi_wedge
.matchhash
, d
->buf
, d
->ll
.secsize
);
258 sectors
= sizeof(d
->buf
)/d
->ll
.secsize
; /* sectors per buffer */
259 entries
= sizeof(d
->buf
)/gpth
.hdr_entsz
; /* entries per buffer */
260 entblk
= gpth
.hdr_lba_table
;
261 crc
= crc32(0, NULL
, 0);
264 ep
= (const struct gpt_ent
*)d
->buf
;
266 for (entry
= 0; entry
< gpth
.hdr_entries
; entry
+= entries
) {
267 size
= MIN(sizeof(d
->buf
),
268 (gpth
.hdr_entries
- entry
) * gpth
.hdr_entsz
);
269 entries
= size
/ gpth
.hdr_entsz
;
270 sectors
= roundup(size
, d
->ll
.secsize
) / d
->ll
.secsize
;
271 if (readsects(&d
->ll
, entblk
, sectors
, d
->buf
, 1))
274 crc
= crc32(crc
, (const void *)d
->buf
, size
);
276 for (i
= 0; j
< BIOSDISKNPART
&& i
< entries
; i
++, j
++) {
277 u
= (const struct uuid
*)ep
[i
].ent_type
;
278 if (!guid_is_nil(u
)) {
279 d
->part
[j
].offset
= ep
[i
].ent_lba_start
;
280 d
->part
[j
].size
= ep
[i
].ent_lba_end
-
281 ep
[i
].ent_lba_start
+ 1;
282 if (guid_is_equal(u
, &GET_nbsd_ffs
))
283 d
->part
[j
].fstype
= FS_BSDFFS
;
284 else if (guid_is_equal(u
, &GET_nbsd_lfs
))
285 d
->part
[j
].fstype
= FS_BSDLFS
;
286 else if (guid_is_equal(u
, &GET_nbsd_raid
))
287 d
->part
[j
].fstype
= FS_RAID
;
288 else if (guid_is_equal(u
, &GET_nbsd_swap
))
289 d
->part
[j
].fstype
= FS_SWAP
;
291 d
->part
[j
].fstype
= FS_OTHER
;
297 if (crc
!= gpth
.hdr_crc_table
) {
299 printf("GPT table CRC invalid\n");
308 read_gpt(struct biosdisk
*d
)
310 struct biosdisk_extinfo ed
;
311 daddr_t gptsector
[2];
314 if (d
->ll
.type
!= BIOSDISK_TYPE_HD
)
315 /* No GPT on floppy and CD */
318 gptsector
[0] = GPT_HDR_BLKNO
;
319 if (set_geometry(&d
->ll
, &ed
) == 0 && d
->ll
.flags
& BIOSDISK_INT13EXT
) {
320 gptsector
[1] = ed
.totsec
- 1;
321 /* Sanity check values returned from BIOS */
322 if (ed
.sbytes
>= 512 && (ed
.sbytes
& (ed
.sbytes
- 1)) == 0)
323 d
->ll
.secsize
= ed
.sbytes
;
326 printf("Unable to determine extended disk geometry - "
329 /* at least try some other reasonable values then */
330 gptsector
[1] = d
->ll
.chs_sectors
- 1;
333 for (i
= 0; i
< __arraycount(gptsector
); i
++) {
334 error
= check_gpt(d
, gptsector
[i
]);
339 if (i
>= __arraycount(gptsector
)) {
340 memset(d
->part
, 0, sizeof(d
->part
));
344 #ifndef USE_SECONDARY_GPT
347 printf("ignoring valid secondary GPT\n");
354 printf("using %s GPT\n", (i
== 0) ? "primary" : "secondary");
362 ingest_label(struct biosdisk
*d
, struct disklabel
*lp
)
366 memset(d
->part
, 0, sizeof(d
->part
));
368 for (part
= 0; part
< lp
->d_npartitions
; part
++) {
369 if (lp
->d_partitions
[part
].p_size
== 0)
371 if (lp
->d_partitions
[part
].p_fstype
== FS_UNUSED
)
373 d
->part
[part
].fstype
= lp
->d_partitions
[part
].p_fstype
;
374 d
->part
[part
].offset
= lp
->d_partitions
[part
].p_offset
;
375 d
->part
[part
].size
= lp
->d_partitions
[part
].p_size
;
380 check_label(struct biosdisk
*d
, daddr_t sector
)
382 struct disklabel
*lp
;
384 /* find partition in NetBSD disklabel */
385 if (readsects(&d
->ll
, sector
+ LABELSECTOR
, 1, d
->buf
, 0)) {
387 printf("Error reading disklabel\n");
391 lp
= (struct disklabel
*) (d
->buf
+ LABELOFFSET
);
392 if (lp
->d_magic
!= DISKMAGIC
|| dkcksum(lp
)) {
394 printf("warning: no disklabel in sector %"PRId64
"\n", sector
);
402 bi_disk
.labelsector
= sector
+ LABELSECTOR
;
403 bi_disk
.label
.type
= lp
->d_type
;
404 memcpy(bi_disk
.label
.packname
, lp
->d_packname
, 16);
405 bi_disk
.label
.checksum
= lp
->d_checksum
;
407 bi_wedge
.matchblk
= sector
+ LABELSECTOR
;
408 bi_wedge
.matchnblks
= 1;
410 md5(bi_wedge
.matchhash
, d
->buf
, d
->ll
.secsize
);
417 read_minix_subp(struct biosdisk
*d
, struct disklabel
* dflt_lbl
,
418 int this_ext
, daddr_t sector
)
420 struct mbr_partition mbr
[MBR_PART_COUNT
];
425 if (readsects(&d
->ll
, sector
, 1, d
->buf
, 0)) {
427 printf("Error reading MFS sector %"PRId64
"\n", sector
);
431 if ((uint8_t)d
->buf
[510] != 0x55 || (uint8_t)d
->buf
[511] != 0xAA) {
434 memcpy(&mbr
, MBR_PARTS(d
->buf
), sizeof(mbr
));
435 for (i
= 0; i
< MBR_PART_COUNT
; i
++) {
436 typ
= mbr
[i
].mbrp_type
;
439 sector
= this_ext
+ mbr
[i
].mbrp_start
;
440 if (dflt_lbl
->d_npartitions
>= MAXPARTITIONS
)
442 p
= &dflt_lbl
->d_partitions
[dflt_lbl
->d_npartitions
++];
443 p
->p_offset
= sector
;
444 p
->p_size
= mbr
[i
].mbrp_size
;
445 p
->p_fstype
= xlat_mbr_fstype(typ
);
451 read_label(struct biosdisk
*d
)
453 struct disklabel dflt_lbl
;
454 struct mbr_partition mbr
[MBR_PART_COUNT
];
460 uint32_t ext_base
, this_ext
, next_ext
;
461 #ifdef COMPAT_386BSD_MBRPART
462 int sector_386bsd
= -1;
465 memset(&dflt_lbl
, 0, sizeof(dflt_lbl
));
466 dflt_lbl
.d_npartitions
= 8;
470 if (d
->ll
.type
!= BIOSDISK_TYPE_HD
)
471 /* No label on floppy and CD */
475 * find NetBSD Partition in DOS partition table
481 this_ext
= ext_base
+ next_ext
;
483 if (readsects(&d
->ll
, this_ext
, 1, d
->buf
, 0)) {
485 printf("error reading MBR sector %u\n", this_ext
);
489 memcpy(&mbr
, MBR_PARTS(d
->buf
), sizeof(mbr
));
490 /* Look for NetBSD partition ID */
491 for (i
= 0; i
< MBR_PART_COUNT
; i
++) {
492 typ
= mbr
[i
].mbrp_type
;
495 sector
= this_ext
+ mbr
[i
].mbrp_start
;
497 printf("ptn type %d in sector %u\n", typ
, sector
);
499 if (typ
== MBR_PTYPE_MINIX_14B
) {
500 if (!read_minix_subp(d
, &dflt_lbl
,
502 /* Don't add "container" partition */
506 if (typ
== MBR_PTYPE_NETBSD
) {
507 error
= check_label(d
, sector
);
511 if (MBR_IS_EXTENDED(typ
)) {
512 next_ext
= mbr
[i
].mbrp_start
;
515 #ifdef COMPAT_386BSD_MBRPART
516 if (this_ext
== 0 && typ
== MBR_PTYPE_386BSD
)
517 sector_386bsd
= sector
;
520 if (dflt_lbl
.d_npartitions
>= MAXPARTITIONS
)
522 p
= &dflt_lbl
.d_partitions
[dflt_lbl
.d_npartitions
++];
524 p
= &dflt_lbl
.d_partitions
[i
];
525 p
->p_offset
= sector
;
526 p
->p_size
= mbr
[i
].mbrp_size
;
527 p
->p_fstype
= xlat_mbr_fstype(typ
);
538 #ifdef COMPAT_386BSD_MBRPART
539 if (sector_386bsd
!= -1) {
540 printf("old BSD partition ID!\n");
541 sector
= sector_386bsd
;
548 * 2. no NetBSD partition in MBR
550 * We simply default to "start of disk" in this case and
553 error
= check_label(d
, sector
);
558 * Nothing at start of disk, return info from mbr partitions.
560 /* XXX fill it to make checksum match kernel one */
561 dflt_lbl
.d_checksum
= dkcksum(&dflt_lbl
);
562 ingest_label(d
, &dflt_lbl
);
565 #endif /* NO_DISKLABEL */
567 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
569 read_partitions(struct biosdisk
*d
)
582 error
= read_label(d
);
593 struct biosdisk_extinfo ed
;
597 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
601 for (i
= 0; i
< MAX_BIOSDISKS
+ 2; i
++) {
603 memset(&d
, 0, sizeof(d
));
604 memset(&ed
, 0, sizeof(ed
));
605 if (i
>= MAX_BIOSDISKS
)
606 d
.ll
.dev
= 0x00 + i
- MAX_BIOSDISKS
; /* fd */
608 d
.ll
.dev
= 0x80 + i
; /* hd/cd */
609 if (set_geometry(&d
.ll
, &ed
))
613 case BIOSDISK_TYPE_CD
:
614 printf("cd0\n cd0a\n");
616 case BIOSDISK_TYPE_FD
:
617 printf("fd%d\n", d
.ll
.dev
& 0x7f);
618 printf(" fd%da\n", d
.ll
.dev
& 0x7f);
620 case BIOSDISK_TYPE_HD
:
621 printf("hd%d", d
.ll
.dev
& 0x7f);
622 if (d
.ll
.flags
& BIOSDISK_INT13EXT
) {
624 size
= ed
.totsec
* ed
.sbytes
;
625 if (size
>= (10ULL * 1024 * 1024 * 1024))
626 printf("%"PRIu64
" GB",
627 size
/ (1024 * 1024 * 1024));
629 printf("%"PRIu64
" MB",
630 size
/ (1024 * 1024));
635 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
636 if (d
.ll
.type
!= BIOSDISK_TYPE_HD
)
639 if (read_partitions(&d
) != 0)
642 for (part
= 0; part
< BIOSDISKNPART
; part
++) {
643 if (d
.part
[part
].size
== 0)
645 if (d
.part
[part
].fstype
== FS_UNUSED
)
651 printf(" hd%d%c(", d
.ll
.dev
& 0x7f, part
+ 'a');
652 if (d
.part
[part
].fstype
< FSMAXTYPES
)
654 fstypenames
[d
.part
[part
].fstype
]);
656 printf("%d", d
.part
[part
].fstype
);
665 /* Determine likely partition for possible sector number of dos
670 biosdisk_findpartition(int biosdev
, daddr_t sector
)
672 #if defined(NO_DISKLABEL) && defined(NO_GPT)
678 printf("looking for partition device %x, sector %"PRId64
"\n", biosdev
, sector
);
681 /* Look for netbsd partition that is the dos boot one */
682 d
= alloc_biosdisk(biosdev
);
686 if (read_partitions(d
) == 0) {
687 for (partition
= (BIOSDISKNPART
-1); --partition
;) {
688 if (d
->part
[partition
].fstype
== FS_UNUSED
)
690 if (d
->part
[partition
].offset
== sector
)
695 dealloc(d
, sizeof(*d
));
697 #endif /* NO_DISKLABEL && NO_GPT */
702 add_biosdisk_bootinfo(void)
706 if (bootinfo
== NULL
) {
714 BI_ADD(&bi_disk
, BTINFO_BOOTDISK
, sizeof(bi_disk
));
715 BI_ADD(&bi_wedge
, BTINFO_BOOTWEDGE
, sizeof(bi_wedge
));
725 biosdisk_open(struct open_file
*f
, ...)
726 /* struct open_file *f, int biosdev, int partition */
735 biosdev
= va_arg(ap
, int);
736 d
= alloc_biosdisk(biosdev
);
742 partition
= va_arg(ap
, int);
744 bi_disk
.biosdev
= d
->ll
.dev
;
745 bi_disk
.partition
= partition
;
746 bi_disk
.labelsector
= -1;
748 bi_wedge
.biosdev
= d
->ll
.dev
;
749 bi_wedge
.matchblk
= -1;
752 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
753 error
= read_partitions(d
);
761 if (partition
>= BIOSDISKNPART
||
762 d
->part
[partition
].fstype
== FS_UNUSED
) {
764 printf("illegal partition\n");
770 d
->boff
= d
->part
[partition
].offset
;
772 if (d
->part
[partition
].fstype
== FS_RAID
)
773 d
->boff
+= RF_PROTECTED_SECTORS
;
776 bi_wedge
.startblk
= d
->part
[partition
].offset
;
777 bi_wedge
.nblks
= d
->part
[partition
].size
;
783 printf("partition @%"PRId64
"\n", d
->boff
);
787 add_biosdisk_bootinfo();
794 dealloc(d
, sizeof(*d
));
798 #ifndef LIBSA_NO_FS_CLOSE
800 biosdisk_close(struct open_file
*f
)
802 struct biosdisk
*d
= f
->f_devdata
;
804 /* let the floppy drive go off */
805 if (d
->ll
.type
== BIOSDISK_TYPE_FD
)
806 wait_sec(3); /* 2s is enough on all PCs I found */
808 dealloc(d
, sizeof(*d
));
815 biosdisk_ioctl(struct open_file
*f
, u_long cmd
, void *arg
)