summaryrefslogtreecommitdiff
path: root/sys/arch/amd64/stand/efiboot/diskprobe.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/amd64/stand/efiboot/diskprobe.c')
-rw-r--r--sys/arch/amd64/stand/efiboot/diskprobe.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/sys/arch/amd64/stand/efiboot/diskprobe.c b/sys/arch/amd64/stand/efiboot/diskprobe.c
new file mode 100644
index 00000000000..4ddc5ae84a4
--- /dev/null
+++ b/sys/arch/amd64/stand/efiboot/diskprobe.c
@@ -0,0 +1,301 @@
+/* $OpenBSD: diskprobe.c,v 1.1 2019/05/10 21:20:42 mlarkin Exp $ */
+
+/*
+ * Copyright (c) 1997 Tobias Weingartner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* We want the disk type names from disklabel.h */
+#undef DKTYPENAMES
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+#include <sys/hibernate.h>
+
+#include <lib/libz/zlib.h>
+#include <machine/biosvar.h>
+#include <stand/boot/bootarg.h>
+
+#include "disk.h"
+#include "biosdev.h"
+#include "libsa.h"
+
+#ifdef SOFTRAID
+#include "softraid_amd64.h"
+#endif
+#include "efidev.h"
+
+#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */
+
+/* Local Prototypes */
+static int disksum(int);
+
+int bootdev_has_hibernate(void); /* export for loadfile() */
+
+/* List of disk devices we found/probed */
+struct disklist_lh disklist;
+
+/* Pointer to boot device */
+struct diskinfo *bootdev_dip;
+
+extern int debug;
+extern int bios_bootdev;
+extern int bios_cddev;
+
+static void
+efi_hardprobe(void)
+{
+ int n;
+ struct diskinfo *dip, *dipt;
+ u_int bsdunit, type = 0;
+ u_int scsi= 0, ide = 0, atapi = 0;
+ extern struct disklist_lh
+ efi_disklist;
+
+ n = 0;
+ TAILQ_FOREACH_SAFE(dip, &efi_disklist, list, dipt) {
+ TAILQ_REMOVE(&efi_disklist, dip, list);
+ n = scsi + ide;
+
+ /* Try to find the label, to figure out device type */
+ if ((efi_getdisklabel(dip->efi_info, &dip->disklabel))) {
+ type = 0;
+ printf(" hd%d*", n);
+ bsdunit = ide++;
+ } else {
+ /* Best guess */
+ switch (dip->disklabel.d_type) {
+ case DTYPE_SCSI:
+ type = 4;
+ bsdunit = scsi++;
+ dip->bios_info.flags |= BDI_GOODLABEL;
+ break;
+
+ case DTYPE_ESDI:
+ case DTYPE_ST506:
+ type = 0;
+ bsdunit = ide++;
+ dip->bios_info.flags |= BDI_GOODLABEL;
+ break;
+
+ case DTYPE_ATAPI:
+ type = 6;
+ n = atapi;
+ bsdunit = atapi++;
+ dip->bios_info.flags |= BDI_GOODLABEL
+ | BDI_EL_TORITO;
+ break;
+
+ default:
+ dip->bios_info.flags |= BDI_BADLABEL;
+ type = 0; /* XXX Suggest IDE */
+ bsdunit = ide++;
+ }
+ printf(" %cd%d", (type == 6)? 'c' : 'h', n);
+ }
+ if (type != 6)
+ dip->bios_info.bios_number = 0x80 | n;
+ else
+ dip->bios_info.bios_number = 0xe0 | n;
+
+ dip->bios_info.checksum = 0; /* just in case */
+ /* Fill out best we can */
+ dip->bsddev = dip->bios_info.bsd_dev =
+ MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
+ check_hibernate(dip);
+
+ /* Add to queue of disks */
+ TAILQ_INSERT_TAIL(&disklist, dip, list);
+ n++;
+ }
+}
+
+/* Probe for all BIOS supported disks */
+u_int32_t bios_cksumlen;
+void
+diskprobe(void)
+{
+ struct diskinfo *dip;
+ int i;
+
+ /* These get passed to kernel */
+ bios_diskinfo_t *bios_diskinfo;
+
+ /* Init stuff */
+ TAILQ_INIT(&disklist);
+
+ efi_hardprobe();
+
+#ifdef SOFTRAID
+ srprobe();
+#endif
+
+ /* Checksumming of hard disks */
+ for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
+ ;
+ bios_cksumlen = i;
+
+ /* Get space for passing bios_diskinfo stuff to kernel */
+ for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
+ dip = TAILQ_NEXT(dip, list))
+ i++;
+ bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
+
+ /* Copy out the bios_diskinfo stuff */
+ for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
+ dip = TAILQ_NEXT(dip, list))
+ bios_diskinfo[i++] = dip->bios_info;
+
+ bios_diskinfo[i++].bios_number = -1;
+ /* Register for kernel use */
+ addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
+ addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
+ bios_diskinfo);
+}
+
+/* Find info on given BIOS disk */
+struct diskinfo *
+dklookup(int dev)
+{
+ struct diskinfo *dip;
+
+ for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
+ if (dip->bios_info.bios_number == dev)
+ return dip;
+
+ return NULL;
+}
+
+void
+dump_diskinfo(void)
+{
+ struct diskinfo *dip;
+
+ printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
+ for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
+ bios_diskinfo_t *bdi = &dip->bios_info;
+ int d = bdi->bios_number;
+ int u = d & 0x7f;
+ char c;
+
+ if (bdi->flags & BDI_EL_TORITO) {
+ c = 'c';
+ u = 0;
+ } else {
+ c = (d & 0x80) ? 'h' : 'f';
+ }
+
+ printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
+ c, u, d,
+ (bdi->flags & BDI_BADLABEL)?"*none*":"label",
+ bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
+ bdi->flags, bdi->checksum);
+ }
+}
+
+/* Find BIOS portion on given BIOS disk
+ * XXX - Use dklookup() instead.
+ */
+bios_diskinfo_t *
+bios_dklookup(int dev)
+{
+ struct diskinfo *dip;
+
+ dip = dklookup(dev);
+ if (dip)
+ return &dip->bios_info;
+
+ return NULL;
+}
+
+/*
+ * Checksum one more block on all harddrives
+ *
+ * Use the adler32() function from libz,
+ * as it is quick, small, and available.
+ */
+int
+disksum(int blk)
+{
+ struct diskinfo *dip, *dip2;
+ int st, reprobe = 0;
+ char buf[DEV_BSIZE];
+
+ for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
+ bios_diskinfo_t *bdi = &dip->bios_info;
+
+ /* Skip this disk if it is not a HD or has had an I/O error */
+ if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
+ continue;
+
+ /* Adler32 checksum */
+ st = dip->diskio(F_READ, dip, blk, 1, buf);
+ if (st) {
+ bdi->flags |= BDI_INVALID;
+ continue;
+ }
+ bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
+
+ for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
+ dip2 = TAILQ_NEXT(dip2, list)) {
+ bios_diskinfo_t *bd = &dip2->bios_info;
+ if ((bd->bios_number & 0x80) &&
+ !(bd->flags & BDI_INVALID) &&
+ bdi->checksum == bd->checksum)
+ reprobe = 1;
+ }
+ }
+
+ return reprobe;
+}
+
+int
+bootdev_has_hibernate(void)
+{
+ return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0);
+}
+
+void
+check_hibernate(struct diskinfo *dip)
+{
+ daddr_t sec;
+ int error;
+ union hibernate_info hib;
+
+ /* read hibernate */
+ if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP ||
+ DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0)
+ return;
+
+ sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) +
+ DL_GETPSIZE(&dip->disklabel.d_partitions[1]) -
+ (sizeof(union hibernate_info) / DEV_BSIZE);
+
+ error = dip->strategy(dip, F_READ, (daddr32_t)sec, sizeof hib, &hib, NULL);
+ if (error == 0 && hib.magic == HIBERNATE_MAGIC)
+ dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */
+}