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