1 1.4 christos /* $NetBSD: diskprobe.c,v 1.4 2015/01/02 19:42:06 christos Exp $ */ 2 1.1 nonaka /* $OpenBSD: diskprobe.c,v 1.3 2006/10/13 00:00:55 krw Exp $ */ 3 1.1 nonaka 4 1.1 nonaka /* 5 1.1 nonaka * Copyright (c) 1997 Tobias Weingartner 6 1.1 nonaka * All rights reserved. 7 1.1 nonaka * 8 1.1 nonaka * Redistribution and use in source and binary forms, with or without 9 1.1 nonaka * modification, are permitted provided that the following conditions 10 1.1 nonaka * are met: 11 1.1 nonaka * 1. Redistributions of source code must retain the above copyright 12 1.1 nonaka * notice, this list of conditions and the following disclaimer. 13 1.1 nonaka * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 nonaka * notice, this list of conditions and the following disclaimer in the 15 1.1 nonaka * documentation and/or other materials provided with the distribution. 16 1.1 nonaka * 17 1.1 nonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 1.1 nonaka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 1.1 nonaka * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 nonaka * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 1.1 nonaka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 nonaka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 nonaka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 nonaka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 nonaka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 nonaka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 nonaka * SUCH DAMAGE. 28 1.1 nonaka * 29 1.1 nonaka */ 30 1.1 nonaka 31 1.1 nonaka /* We want the disk type names from disklabel.h */ 32 1.1 nonaka #undef DKTYPENAMES 33 1.1 nonaka 34 1.1 nonaka #include <sys/param.h> 35 1.1 nonaka #include <sys/bootblock.h> 36 1.1 nonaka #include <sys/disklabel.h> 37 1.1 nonaka #include <sys/queue.h> 38 1.1 nonaka #include <sys/reboot.h> 39 1.1 nonaka 40 1.1 nonaka #include "boot.h" 41 1.1 nonaka #include "disk.h" 42 1.1 nonaka #include "unixdev.h" 43 1.2 nonaka #include "pathnames.h" 44 1.1 nonaka #include "compat_linux.h" 45 1.1 nonaka 46 1.2 nonaka /* All the info on /proc/partitions */ 47 1.2 nonaka struct partinfo { 48 1.2 nonaka char devname[MAXDEVNAME]; 49 1.2 nonaka TAILQ_ENTRY(partinfo) list; 50 1.2 nonaka }; 51 1.2 nonaka TAILQ_HEAD(partlist_lh, partinfo); 52 1.2 nonaka struct partlist_lh partlist; 53 1.2 nonaka 54 1.1 nonaka /* Disk spin-up wait timeout. */ 55 1.1 nonaka static u_int timeout = 10; 56 1.1 nonaka 57 1.1 nonaka /* List of disk devices we found/probed */ 58 1.1 nonaka struct disklist_lh disklist; 59 1.1 nonaka 60 1.1 nonaka static char disk_devname[MAXDEVNAME]; 61 1.1 nonaka 62 1.1 nonaka /* 63 1.1 nonaka * Probe for all hard disks. 64 1.1 nonaka */ 65 1.1 nonaka static void 66 1.1 nonaka hardprobe(char *buf, size_t bufsiz) 67 1.1 nonaka { 68 1.1 nonaka /* XXX probe disks in this order */ 69 1.1 nonaka static const int order[] = { 0x80, 0x82, 0x00 }; 70 1.1 nonaka char devname[MAXDEVNAME]; 71 1.1 nonaka struct diskinfo *dip; 72 1.1 nonaka u_int hd_disk = 0; 73 1.1 nonaka u_int mmcd_disk = 0; 74 1.1 nonaka uint unit = 0; 75 1.1 nonaka int first = 1; 76 1.1 nonaka int i; 77 1.1 nonaka 78 1.1 nonaka buf[0] = '\0'; 79 1.1 nonaka 80 1.1 nonaka /* Hard disks */ 81 1.1 nonaka for (i = 0; i < __arraycount(order); i++) { 82 1.1 nonaka dip = alloc(sizeof(struct diskinfo)); 83 1.1 nonaka memset(dip, 0, sizeof(*dip)); 84 1.1 nonaka 85 1.1 nonaka if (bios_getdiskinfo(order[i], &dip->bios_info) != NULL) { 86 1.1 nonaka dealloc(dip, 0); 87 1.1 nonaka continue; 88 1.1 nonaka } 89 1.1 nonaka 90 1.1 nonaka bios_devname(order[i], devname, sizeof(devname)); 91 1.1 nonaka if (order[i] & 0x80) { 92 1.3 nonaka unit = hd_disk++; 93 1.1 nonaka } else { 94 1.3 nonaka unit = mmcd_disk++; 95 1.1 nonaka } 96 1.3 nonaka snprintf(dip->devname, sizeof(dip->devname), "%s%d", devname, 97 1.3 nonaka unit); 98 1.1 nonaka strlcat(buf, dip->devname, bufsiz); 99 1.1 nonaka 100 1.1 nonaka /* Try to find the label, to figure out device type. */ 101 1.1 nonaka if (bios_getdisklabel(&dip->bios_info, &dip->disklabel) 102 1.1 nonaka == NULL) { 103 1.1 nonaka strlcat(buf, "*", bufsiz); 104 1.1 nonaka if (first) { 105 1.1 nonaka first = 0; 106 1.1 nonaka strlcpy(disk_devname, devname, 107 1.1 nonaka sizeof(disk_devname)); 108 1.1 nonaka default_devname = disk_devname; 109 1.1 nonaka default_unit = unit; 110 1.3 nonaka default_partition = 0; 111 1.1 nonaka } 112 1.1 nonaka } else { 113 1.1 nonaka /* Best guess */ 114 1.1 nonaka switch (dip->disklabel.d_type) { 115 1.4 christos case DKTYPE_SCSI: 116 1.4 christos case DKTYPE_ESDI: 117 1.4 christos case DKTYPE_ST506: 118 1.1 nonaka dip->bios_info.flags |= BDI_GOODLABEL; 119 1.1 nonaka break; 120 1.1 nonaka 121 1.1 nonaka default: 122 1.1 nonaka dip->bios_info.flags |= BDI_BADLABEL; 123 1.1 nonaka } 124 1.1 nonaka } 125 1.1 nonaka 126 1.1 nonaka /* Add to queue of disks. */ 127 1.1 nonaka TAILQ_INSERT_TAIL(&disklist, dip, list); 128 1.1 nonaka 129 1.1 nonaka strlcat(buf, " ", bufsiz); 130 1.1 nonaka } 131 1.3 nonaka 132 1.3 nonaka /* path */ 133 1.3 nonaka strlcat(buf, devname_path, bufsiz); 134 1.3 nonaka strlcat(buf, "*", bufsiz); 135 1.3 nonaka if (first) { 136 1.3 nonaka first = 0; 137 1.3 nonaka strlcpy(disk_devname, devname_path, sizeof(disk_devname)); 138 1.3 nonaka default_devname = disk_devname; 139 1.3 nonaka default_unit = 0; 140 1.3 nonaka default_partition = 0; 141 1.3 nonaka } 142 1.1 nonaka } 143 1.1 nonaka 144 1.2 nonaka static void 145 1.2 nonaka getpartitions(void) 146 1.2 nonaka { 147 1.2 nonaka struct linux_stat sb; 148 1.2 nonaka struct partinfo *pip; 149 1.2 nonaka char *bc, *top, *next, *p, *q; 150 1.2 nonaka int fd, off, len; 151 1.2 nonaka 152 1.2 nonaka fd = uopen(_PATH_PARTITIONS, LINUX_O_RDONLY); 153 1.2 nonaka if (fd == -1) 154 1.2 nonaka return; 155 1.2 nonaka 156 1.2 nonaka if (ufstat(fd, &sb) < 0) { 157 1.2 nonaka uclose(fd); 158 1.2 nonaka return; 159 1.2 nonaka } 160 1.2 nonaka 161 1.2 nonaka bc = alloc(sb.lst_size + 1); 162 1.2 nonaka if (bc == NULL) { 163 1.2 nonaka printf("Could not allocate memory for %s\n", _PATH_PARTITIONS); 164 1.2 nonaka uclose(fd); 165 1.2 nonaka return; 166 1.2 nonaka } 167 1.2 nonaka 168 1.2 nonaka off = 0; 169 1.2 nonaka do { 170 1.2 nonaka len = uread(fd, bc + off, 1024); 171 1.2 nonaka if (len <= 0) 172 1.2 nonaka break; 173 1.2 nonaka off += len; 174 1.2 nonaka } while (len > 0); 175 1.2 nonaka bc[off] = '\0'; 176 1.2 nonaka 177 1.2 nonaka uclose(fd); 178 1.2 nonaka 179 1.2 nonaka /* bc now contains the whole /proc/partitions */ 180 1.2 nonaka for (p = bc; *p != '\0'; p = next) { 181 1.2 nonaka top = p; 182 1.2 nonaka 183 1.2 nonaka /* readline */ 184 1.2 nonaka for (; *p != '\0' && *p != '\r' && *p != '\n'; p++) 185 1.2 nonaka continue; 186 1.2 nonaka if (*p == '\r') { 187 1.2 nonaka *p++ = '\0'; 188 1.2 nonaka if (*p == '\n') 189 1.2 nonaka *p++ = '\0'; 190 1.2 nonaka } else if (*p == '\n') 191 1.2 nonaka *p++ = '\0'; 192 1.2 nonaka next = p; 193 1.2 nonaka 194 1.2 nonaka /* 195 1.2 nonaka * /proc/partitions format: 196 1.2 nonaka * major minor #blocks name 197 1.2 nonaka * 198 1.2 nonaka * %d %d %d %s 199 1.2 nonaka * 200 1.2 nonaka * e.g.: 201 1.2 nonaka * major minor #blocks name 202 1.2 nonaka * 203 1.2 nonaka * 22 0 7962192 hdc 204 1.2 nonaka * 22 1 10079 hdc1 205 1.2 nonaka * 60 0 965120 mmcda 206 1.2 nonaka * 60 1 43312 mmcda1 207 1.2 nonaka */ 208 1.2 nonaka 209 1.2 nonaka /* trailing space */ 210 1.2 nonaka for (p = top; *p == ' ' || *p == '\t'; p++) 211 1.2 nonaka continue; 212 1.2 nonaka 213 1.2 nonaka /* major */ 214 1.2 nonaka for (; isdigit(*p); p++) 215 1.2 nonaka continue; 216 1.2 nonaka if (*p != ' ' && *p != '\t') 217 1.2 nonaka continue; /* next line */ 218 1.2 nonaka for (; *p == ' ' || *p == '\t'; p++) 219 1.2 nonaka continue; 220 1.2 nonaka 221 1.2 nonaka /* minor */ 222 1.2 nonaka for (; isdigit(*p); p++) 223 1.2 nonaka continue; 224 1.2 nonaka if (*p != ' ' && *p != '\t') 225 1.2 nonaka continue; /* next line */ 226 1.2 nonaka for (; *p == ' ' || *p == '\t'; p++) 227 1.2 nonaka continue; 228 1.2 nonaka 229 1.2 nonaka /* #blocks */ 230 1.2 nonaka for (; isdigit(*p); p++) 231 1.2 nonaka continue; 232 1.2 nonaka if (*p != ' ' && *p != '\t') 233 1.2 nonaka continue; /* next line */ 234 1.2 nonaka for (; *p == ' ' || *p == '\t'; p++) 235 1.2 nonaka continue; 236 1.2 nonaka 237 1.2 nonaka /* name */ 238 1.2 nonaka for (q = p; isalpha(*p) || isdigit(*p); p++) 239 1.2 nonaka continue; 240 1.2 nonaka if (*p != ' ' && *p != '\t' && *p != '\0') 241 1.2 nonaka continue; /* next line */ 242 1.2 nonaka if (isdigit(p[-1])) 243 1.2 nonaka continue; /* next line */ 244 1.2 nonaka *p = '\0'; 245 1.2 nonaka 246 1.2 nonaka pip = alloc(sizeof(*pip)); 247 1.2 nonaka if (pip == NULL) { 248 1.2 nonaka printf("Could not allocate memory for partition\n"); 249 1.2 nonaka continue; /* next line */ 250 1.2 nonaka } 251 1.2 nonaka memset(pip, 0, sizeof(*pip)); 252 1.2 nonaka snprintf(pip->devname, sizeof(pip->devname), "/dev/%s", q); 253 1.2 nonaka TAILQ_INSERT_TAIL(&partlist, pip, list); 254 1.2 nonaka } 255 1.2 nonaka 256 1.2 nonaka dealloc(bc, 0); 257 1.2 nonaka } 258 1.2 nonaka 259 1.1 nonaka /* Probe for all BIOS supported disks */ 260 1.1 nonaka void 261 1.1 nonaka diskprobe(char *buf, size_t bufsiz) 262 1.1 nonaka { 263 1.1 nonaka 264 1.2 nonaka /* get available disk list from /proc/partitions */ 265 1.2 nonaka TAILQ_INIT(&partlist); 266 1.2 nonaka getpartitions(); 267 1.2 nonaka 268 1.1 nonaka /* Init stuff */ 269 1.1 nonaka TAILQ_INIT(&disklist); 270 1.1 nonaka 271 1.1 nonaka /* Do probes */ 272 1.1 nonaka hardprobe(buf, bufsiz); 273 1.1 nonaka } 274 1.1 nonaka 275 1.1 nonaka /* 276 1.1 nonaka * Find info on the disk given by major + unit number. 277 1.1 nonaka */ 278 1.1 nonaka struct diskinfo * 279 1.1 nonaka dkdevice(const char *devname, uint unit) 280 1.1 nonaka { 281 1.1 nonaka char name[MAXDEVNAME]; 282 1.1 nonaka struct diskinfo *dip; 283 1.1 nonaka 284 1.1 nonaka snprintf(name, sizeof(name), "%s%d", devname, unit); 285 1.1 nonaka for (dip = TAILQ_FIRST(&disklist); dip != NULL; 286 1.1 nonaka dip = TAILQ_NEXT(dip, list)) { 287 1.1 nonaka if (strcmp(name, dip->devname) == 0) { 288 1.1 nonaka return dip; 289 1.1 nonaka } 290 1.1 nonaka } 291 1.1 nonaka return NULL; 292 1.1 nonaka } 293 1.1 nonaka 294 1.1 nonaka int 295 1.1 nonaka bios_devname(int biosdev, char *devname, int size) 296 1.1 nonaka { 297 1.1 nonaka 298 1.1 nonaka if ((biosdev & 0x80) != 0) { 299 1.1 nonaka strlcpy(devname, devname_hd, size); 300 1.1 nonaka } else { 301 1.1 nonaka strlcpy(devname, devname_mmcd, size); 302 1.1 nonaka } 303 1.1 nonaka return 0; 304 1.1 nonaka } 305 1.1 nonaka 306 1.1 nonaka /* 307 1.1 nonaka * Find the Linux device path that corresponds to the given "BIOS" disk, 308 1.1 nonaka * where 0x80 corresponds to /dev/hda, 0x81 to /dev/hdb, and so on. 309 1.1 nonaka */ 310 1.1 nonaka void 311 1.1 nonaka bios_devpath(int dev, int part, char *p) 312 1.1 nonaka { 313 1.1 nonaka char devname[MAXDEVNAME]; 314 1.1 nonaka const char *q; 315 1.1 nonaka 316 1.1 nonaka *p++ = '/'; 317 1.1 nonaka *p++ = 'd'; 318 1.1 nonaka *p++ = 'e'; 319 1.1 nonaka *p++ = 'v'; 320 1.1 nonaka *p++ = '/'; 321 1.1 nonaka 322 1.1 nonaka bios_devname(dev, devname, sizeof(devname)); 323 1.1 nonaka q = devname; 324 1.1 nonaka while (*q != '\0') 325 1.1 nonaka *p++ = *q++; 326 1.1 nonaka 327 1.1 nonaka *p++ = 'a' + (dev & 0x7f); 328 1.1 nonaka if (part >= 0) 329 1.1 nonaka *p++ = '1' + part; 330 1.1 nonaka *p = '\0'; 331 1.1 nonaka } 332 1.1 nonaka 333 1.1 nonaka /* 334 1.1 nonaka * Fill out a bios_diskinfo_t for this device. 335 1.1 nonaka */ 336 1.1 nonaka char * 337 1.1 nonaka bios_getdiskinfo(int dev, bios_diskinfo_t *bdi) 338 1.1 nonaka { 339 1.1 nonaka static char path[PATH_MAX]; 340 1.1 nonaka struct linux_stat sb; 341 1.2 nonaka struct partinfo *pip; 342 1.1 nonaka 343 1.1 nonaka memset(bdi, 0, sizeof *bdi); 344 1.1 nonaka bdi->bios_number = -1; 345 1.1 nonaka 346 1.1 nonaka bios_devpath(dev, -1, path); 347 1.1 nonaka 348 1.2 nonaka /* Check device name in /proc/partitions */ 349 1.2 nonaka for (pip = TAILQ_FIRST(&partlist); pip != NULL; 350 1.2 nonaka pip = TAILQ_NEXT(pip, list)) { 351 1.2 nonaka if (!strcmp(path, pip->devname)) 352 1.2 nonaka break; 353 1.2 nonaka } 354 1.2 nonaka if (pip == NULL) 355 1.2 nonaka return "no device node"; 356 1.2 nonaka 357 1.1 nonaka if (ustat(path, &sb) != 0) 358 1.1 nonaka return "no device node"; 359 1.1 nonaka 360 1.1 nonaka bdi->bios_number = dev; 361 1.1 nonaka 362 1.1 nonaka if (bios_getdospart(bdi) < 0) 363 1.1 nonaka return "no NetBSD partition"; 364 1.1 nonaka 365 1.1 nonaka return NULL; 366 1.1 nonaka } 367 1.1 nonaka 368 1.1 nonaka int 369 1.1 nonaka bios_getdospart(bios_diskinfo_t *bdi) 370 1.1 nonaka { 371 1.1 nonaka char path[PATH_MAX]; 372 1.1 nonaka char buf[DEV_BSIZE]; 373 1.1 nonaka struct mbr_partition *mp; 374 1.1 nonaka int fd; 375 1.1 nonaka u_int part; 376 1.1 nonaka size_t rsize; 377 1.1 nonaka 378 1.1 nonaka bios_devpath(bdi->bios_number, -1, path); 379 1.1 nonaka 380 1.1 nonaka /* 381 1.1 nonaka * Give disk devices some time to become ready when the first open 382 1.1 nonaka * fails. Even when open succeeds the disk is sometimes not ready. 383 1.1 nonaka */ 384 1.1 nonaka if ((fd = uopen(path, LINUX_O_RDONLY)) == -1 && errno == ENXIO) { 385 1.1 nonaka while (fd == -1 && timeout > 0) { 386 1.1 nonaka timeout--; 387 1.1 nonaka sleep(1); 388 1.1 nonaka fd = uopen(path, LINUX_O_RDONLY); 389 1.1 nonaka } 390 1.1 nonaka if (fd != -1) 391 1.1 nonaka sleep(2); 392 1.1 nonaka } 393 1.1 nonaka if (fd == -1) 394 1.1 nonaka return -1; 395 1.1 nonaka 396 1.1 nonaka /* Read the disk's MBR. */ 397 1.1 nonaka if (unixstrategy((void *)fd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, 398 1.1 nonaka &rsize) != 0 || rsize != DEV_BSIZE) { 399 1.1 nonaka uclose(fd); 400 1.1 nonaka errno = EIO; 401 1.1 nonaka return -1; 402 1.1 nonaka } 403 1.1 nonaka 404 1.1 nonaka /* Find NetBSD primary partition in the disk's MBR. */ 405 1.1 nonaka mp = (struct mbr_partition *)&buf[MBR_PART_OFFSET]; 406 1.1 nonaka for (part = 0; part < MBR_PART_COUNT; part++) { 407 1.1 nonaka if (mp[part].mbrp_type == MBR_PTYPE_NETBSD) 408 1.1 nonaka break; 409 1.1 nonaka } 410 1.1 nonaka if (part == MBR_PART_COUNT) { 411 1.1 nonaka uclose(fd); 412 1.1 nonaka errno = ERDLAB; 413 1.1 nonaka return -1; 414 1.1 nonaka } 415 1.1 nonaka uclose(fd); 416 1.1 nonaka 417 1.1 nonaka return part; 418 1.1 nonaka } 419 1.1 nonaka 420 1.1 nonaka char * 421 1.1 nonaka bios_getdisklabel(bios_diskinfo_t *bdi, struct disklabel *label) 422 1.1 nonaka { 423 1.1 nonaka char path[PATH_MAX]; 424 1.1 nonaka char buf[DEV_BSIZE]; 425 1.1 nonaka int part; 426 1.1 nonaka int fd; 427 1.1 nonaka size_t rsize; 428 1.1 nonaka 429 1.1 nonaka part = bios_getdospart(bdi); 430 1.1 nonaka if (part < 0) 431 1.1 nonaka return "no NetBSD partition"; 432 1.1 nonaka 433 1.1 nonaka bios_devpath(bdi->bios_number, part, path); 434 1.1 nonaka 435 1.1 nonaka /* Test if the NetBSD partition has a valid disklabel. */ 436 1.1 nonaka if ((fd = uopen(path, LINUX_O_RDONLY)) != -1) { 437 1.1 nonaka char *msg = "failed to read disklabel"; 438 1.1 nonaka 439 1.1 nonaka if (unixstrategy((void *)fd, F_READ, LABELSECTOR, 440 1.1 nonaka DEV_BSIZE, buf, &rsize) == 0 && rsize == DEV_BSIZE) 441 1.1 nonaka msg = getdisklabel(buf, label); 442 1.1 nonaka uclose(fd); 443 1.1 nonaka /* Don't wait for other disks if this label is ok. */ 444 1.1 nonaka if (msg == NULL) 445 1.1 nonaka timeout = 0; 446 1.1 nonaka return msg; 447 1.1 nonaka } 448 1.1 nonaka 449 1.1 nonaka return "failed to open partition"; 450 1.1 nonaka } 451