1/* $OpenBSD: diskprobe.c,v 1.3 2024/06/04 20:31:35 krw Exp $ */ 2 3/* 4 * Copyright (c) 1997 Tobias Weingartner 5 * 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 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30/* We want the disk type names from disklabel.h */ 31#undef DKTYPENAMES 32 33#include <sys/param.h> 34#include <sys/queue.h> 35#include <sys/reboot.h> 36#include <sys/disklabel.h> 37#include <sys/hibernate.h> 38 39#include <lib/libz/zlib.h> 40#include <machine/biosvar.h> 41#include <stand/boot/bootarg.h> 42 43#include "disk.h" 44#include "biosdev.h" 45#include "libsa.h" 46 47#ifdef SOFTRAID 48#include "softraid_amd64.h" 49#endif 50#include "efidev.h" 51 52#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ 53 54/* Local Prototypes */ 55static int disksum(int); 56 57int bootdev_has_hibernate(void); /* export for loadfile() */ 58 59/* List of disk devices we found/probed */ 60struct disklist_lh disklist; 61 62/* Pointer to boot device */ 63struct diskinfo *bootdev_dip; 64 65extern int debug; 66extern int bios_bootdev; 67extern int bios_cddev; 68 69static void 70efi_hardprobe(void) 71{ 72 int n; 73 struct diskinfo *dip, *dipt; 74 u_int bsdunit, type = 0; 75 u_int scsi= 0, ide = 0, atapi = 0; 76 extern struct disklist_lh 77 efi_disklist; 78 79 n = 0; 80 TAILQ_FOREACH_SAFE(dip, &efi_disklist, list, dipt) { 81 TAILQ_REMOVE(&efi_disklist, dip, list); 82 n = scsi + ide; 83 84 /* Try to find the label, to figure out device type */ 85 if ((efi_getdisklabel(dip->efi_info, &dip->disklabel))) { 86 type = 0; 87 printf(" hd%d*", n); 88 bsdunit = ide++; 89 } else { 90 /* Best guess */ 91 switch (dip->disklabel.d_type) { 92 case DTYPE_SCSI: 93 type = 4; 94 bsdunit = scsi++; 95 dip->bios_info.flags |= BDI_GOODLABEL; 96 break; 97 98 case DTYPE_ESDI: 99 case DTYPE_ST506: 100 type = 0; 101 bsdunit = ide++; 102 dip->bios_info.flags |= BDI_GOODLABEL; 103 break; 104 105 case DTYPE_ATAPI: 106 type = 6; 107 n = atapi; 108 bsdunit = atapi++; 109 dip->bios_info.flags |= BDI_GOODLABEL 110 | BDI_EL_TORITO; 111 break; 112 113 default: 114 dip->bios_info.flags |= BDI_BADLABEL; 115 type = 0; /* XXX Suggest IDE */ 116 bsdunit = ide++; 117 } 118 printf(" %cd%d", (type == 6)? 'c' : 'h', n); 119 } 120 if (type != 6) 121 dip->bios_info.bios_number = 0x80 | n; 122 else 123 dip->bios_info.bios_number = 0xe0 | n; 124 125 dip->bios_info.checksum = 0; /* just in case */ 126 /* Fill out best we can */ 127 dip->bsddev = dip->bios_info.bsd_dev = 128 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 129 check_hibernate(dip); 130 131 /* Add to queue of disks */ 132 TAILQ_INSERT_TAIL(&disklist, dip, list); 133 n++; 134 } 135} 136 137/* Probe for all BIOS supported disks */ 138u_int32_t bios_cksumlen; 139void 140diskprobe(void) 141{ 142 struct diskinfo *dip; 143 int i; 144 145 /* These get passed to kernel */ 146 bios_diskinfo_t *bios_diskinfo; 147 148 /* Init stuff */ 149 TAILQ_INIT(&disklist); 150 151 efi_hardprobe(); 152 153#ifdef SOFTRAID 154 srprobe(); 155#endif 156 157 /* Checksumming of hard disks */ 158 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 159 ; 160 bios_cksumlen = i; 161 162 /* Get space for passing bios_diskinfo stuff to kernel */ 163 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 164 dip = TAILQ_NEXT(dip, list)) 165 i++; 166 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 167 168 /* Copy out the bios_diskinfo stuff */ 169 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 170 dip = TAILQ_NEXT(dip, list)) 171 bios_diskinfo[i++] = dip->bios_info; 172 173 bios_diskinfo[i++].bios_number = -1; 174 /* Register for kernel use */ 175 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 176 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), 177 bios_diskinfo); 178} 179 180/* Find info on given BIOS disk */ 181struct diskinfo * 182dklookup(int dev) 183{ 184 struct diskinfo *dip; 185 186 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 187 if (dip->bios_info.bios_number == dev) 188 return dip; 189 190 return NULL; 191} 192 193void 194dump_diskinfo(void) 195{ 196 struct diskinfo *dip; 197 198 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 199 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 200 bios_diskinfo_t *bdi = &dip->bios_info; 201 int d = bdi->bios_number; 202 int u = d & 0x7f; 203 char c; 204 205 if (bdi->flags & BDI_EL_TORITO) { 206 c = 'c'; 207 u = 0; 208 } else { 209 c = (d & 0x80) ? 'h' : 'f'; 210 } 211 212 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 213 c, u, d, 214 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 215 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 216 bdi->flags, bdi->checksum); 217 } 218} 219 220/* Find BIOS portion on given BIOS disk 221 * XXX - Use dklookup() instead. 222 */ 223bios_diskinfo_t * 224bios_dklookup(int dev) 225{ 226 struct diskinfo *dip; 227 228 dip = dklookup(dev); 229 if (dip) 230 return &dip->bios_info; 231 232 return NULL; 233} 234 235/* 236 * Checksum one more block on all harddrives 237 * 238 * Use the adler32() function from libz, 239 * as it is quick, small, and available. 240 */ 241int 242disksum(int blk) 243{ 244 struct diskinfo *dip, *dip2; 245 int st, reprobe = 0; 246 char buf[DEV_BSIZE]; 247 248 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 249 bios_diskinfo_t *bdi = &dip->bios_info; 250 251 /* Skip this disk if it is not a HD or has had an I/O error */ 252 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 253 continue; 254 255 /* Adler32 checksum */ 256 st = dip->diskio(F_READ, dip, blk, 1, buf); 257 if (st) { 258 bdi->flags |= BDI_INVALID; 259 continue; 260 } 261 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 262 263 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 264 dip2 = TAILQ_NEXT(dip2, list)) { 265 bios_diskinfo_t *bd = &dip2->bios_info; 266 if ((bd->bios_number & 0x80) && 267 !(bd->flags & BDI_INVALID) && 268 bdi->checksum == bd->checksum) 269 reprobe = 1; 270 } 271 } 272 273 return reprobe; 274} 275 276int 277bootdev_has_hibernate(void) 278{ 279 return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0); 280} 281 282void 283check_hibernate(struct diskinfo *dip) 284{ 285 uint8_t buf[DEV_BSIZE]; 286 daddr_t sec; 287 int error; 288 union hibernate_info *hib = (union hibernate_info *)&buf; 289 290 /* read hibernate */ 291 if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP || 292 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0) 293 return; 294 295 sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) + 296 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 1; 297 298 error = dip->strategy(dip, F_READ, DL_SECTOBLK(&dip->disklabel, sec), 299 sizeof buf, &buf, NULL); 300 if (error == 0 && hib->magic == HIBERNATE_MAGIC) 301 dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */ 302} 303