1223695Sdfr/*- 2223695Sdfr * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3239058Sae * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 4223695Sdfr * All rights reserved. 5223695Sdfr * 6223695Sdfr * Redistribution and use in source and binary forms, with or without 7223695Sdfr * modification, are permitted provided that the following conditions 8223695Sdfr * are met: 9223695Sdfr * 1. Redistributions of source code must retain the above copyright 10223695Sdfr * notice, this list of conditions and the following disclaimer. 11223695Sdfr * 2. Redistributions in binary form must reproduce the above copyright 12223695Sdfr * notice, this list of conditions and the following disclaimer in the 13223695Sdfr * documentation and/or other materials provided with the distribution. 14223695Sdfr * 15223695Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16223695Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17223695Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18223695Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19223695Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20223695Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21223695Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22223695Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23223695Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24223695Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25223695Sdfr * SUCH DAMAGE. 26223695Sdfr */ 27223695Sdfr 28223695Sdfr#include <sys/cdefs.h> 29223695Sdfr__FBSDID("$FreeBSD: stable/11/stand/common/disk.c 347182 2019-05-06 08:55:23Z tsoome $"); 30223695Sdfr 31239058Sae#include <sys/disk.h> 32241053Sae#include <sys/queue.h> 33223695Sdfr#include <stand.h> 34223695Sdfr#include <stdarg.h> 35223695Sdfr#include <bootstrap.h> 36239058Sae#include <part.h> 37223695Sdfr 38223695Sdfr#include "disk.h" 39223695Sdfr 40223695Sdfr#ifdef DISK_DEBUG 41346483Skevans# define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 42223695Sdfr#else 43346483Skevans# define DPRINTF(fmt, args...) 44223695Sdfr#endif 45223695Sdfr 46239058Saestruct open_disk { 47239058Sae struct ptable *table; 48329099Skevans uint64_t mediasize; 49329099Skevans uint64_t entrysize; 50239058Sae u_int sectorsize; 51239058Sae}; 52223695Sdfr 53239058Saestruct print_args { 54239058Sae struct disk_devdesc *dev; 55239058Sae const char *prefix; 56239058Sae int verbose; 57223695Sdfr}; 58223695Sdfr 59239058Sae/* Convert size to a human-readable number. */ 60223695Sdfrstatic char * 61239058Saedisplay_size(uint64_t size, u_int sectorsize) 62223695Sdfr{ 63223695Sdfr static char buf[80]; 64223695Sdfr char unit; 65223695Sdfr 66239058Sae size = size * sectorsize / 1024; 67223695Sdfr unit = 'K'; 68223695Sdfr if (size >= 10485760000LL) { 69223695Sdfr size /= 1073741824; 70223695Sdfr unit = 'T'; 71223695Sdfr } else if (size >= 10240000) { 72223695Sdfr size /= 1048576; 73223695Sdfr unit = 'G'; 74223695Sdfr } else if (size >= 10000) { 75223695Sdfr size /= 1024; 76223695Sdfr unit = 'M'; 77223695Sdfr } 78346483Skevans sprintf(buf, "%4ld%cB", (long)size, unit); 79223695Sdfr return (buf); 80223695Sdfr} 81223695Sdfr 82296963Sallanjudeint 83329099Skevansptblread(void *d, void *buf, size_t blocks, uint64_t offset) 84223695Sdfr{ 85239058Sae struct disk_devdesc *dev; 86239058Sae struct open_disk *od; 87223695Sdfr 88239058Sae dev = (struct disk_devdesc *)d; 89332154Skevans od = (struct open_disk *)dev->dd.d_opendata; 90329100Skevans 91329100Skevans /* 92329140Skevans * The strategy function assumes the offset is in units of 512 byte 93329140Skevans * sectors. For larger sector sizes, we need to adjust the offset to 94329140Skevans * match the actual sector size. 95329140Skevans */ 96329140Skevans offset *= (od->sectorsize / 512); 97329140Skevans /* 98329100Skevans * As the GPT backup partition is located at the end of the disk, 99329100Skevans * to avoid reading past disk end, flag bcache not to use RA. 100329100Skevans */ 101332154Skevans return (dev->dd.d_dev->dv_strategy(dev, F_READ | F_NORA, offset, 102239058Sae blocks * od->sectorsize, (char *)buf, NULL)); 103223695Sdfr} 104223695Sdfr 105300117Simpstatic int 106239058Saeptable_print(void *arg, const char *pname, const struct ptable_entry *part) 107223695Sdfr{ 108329099Skevans struct disk_devdesc dev; 109239058Sae struct print_args *pa, bsd; 110239058Sae struct open_disk *od; 111239058Sae struct ptable *table; 112239058Sae char line[80]; 113300117Simp int res; 114346483Skevans u_int sectsize; 115346483Skevans uint64_t partsize; 116223695Sdfr 117239058Sae pa = (struct print_args *)arg; 118332154Skevans od = (struct open_disk *)pa->dev->dd.d_opendata; 119346483Skevans sectsize = od->sectorsize; 120346483Skevans partsize = part->end - part->start + 1; 121346483Skevans sprintf(line, " %s%s: %s\t%s\n", pa->prefix, pname, 122346483Skevans parttype2str(part->type), 123346483Skevans pa->verbose ? display_size(partsize, sectsize) : ""); 124300117Simp if (pager_output(line)) 125300117Simp return 1; 126300117Simp res = 0; 127239058Sae if (part->type == PART_FREEBSD) { 128239058Sae /* Open slice with BSD label */ 129332154Skevans dev.dd.d_dev = pa->dev->dd.d_dev; 130332154Skevans dev.dd.d_unit = pa->dev->dd.d_unit; 131329099Skevans dev.d_slice = part->index; 132329099Skevans dev.d_partition = -1; 133346483Skevans if (disk_open(&dev, partsize, sectsize) == 0) { 134346483Skevans /* 135346483Skevans * disk_open() for partition -1 on a bsd slice assumes 136346483Skevans * you want the first bsd partition. Reset things so 137346483Skevans * that we're looking at the start of the raw slice. 138346483Skevans */ 139346483Skevans dev.d_partition = -1; 140346483Skevans dev.d_offset = part->start; 141346483Skevans table = ptable_open(&dev, partsize, sectsize, ptblread); 142329099Skevans if (table != NULL) { 143329099Skevans sprintf(line, " %s%s", pa->prefix, pname); 144329099Skevans bsd.dev = pa->dev; 145329099Skevans bsd.prefix = line; 146329099Skevans bsd.verbose = pa->verbose; 147329099Skevans res = ptable_iterate(table, &bsd, ptable_print); 148329099Skevans ptable_close(table); 149329099Skevans } 150329099Skevans disk_close(&dev); 151329099Skevans } 152223695Sdfr } 153300117Simp 154300117Simp return (res); 155223695Sdfr} 156223695Sdfr 157300117Simpint 158239058Saedisk_print(struct disk_devdesc *dev, char *prefix, int verbose) 159223695Sdfr{ 160239058Sae struct open_disk *od; 161239058Sae struct print_args pa; 162223695Sdfr 163239058Sae /* Disk should be opened */ 164332154Skevans od = (struct open_disk *)dev->dd.d_opendata; 165239058Sae pa.dev = dev; 166239058Sae pa.prefix = prefix; 167239058Sae pa.verbose = verbose; 168300117Simp return (ptable_iterate(od->table, &pa, ptable_print)); 169223695Sdfr} 170223695Sdfr 171239058Saeint 172329099Skevansdisk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) 173291402Szbb{ 174291402Szbb struct open_disk *od; 175291402Szbb int ret; 176291402Szbb 177332154Skevans od = (struct open_disk *)dev->dd.d_opendata; 178332154Skevans ret = dev->dd.d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset, 179291402Szbb blocks * od->sectorsize, buf, NULL); 180291402Szbb 181291402Szbb return (ret); 182291402Szbb} 183291402Szbb 184291402Szbbint 185329099Skevansdisk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) 186291402Szbb{ 187291402Szbb struct open_disk *od; 188291402Szbb int ret; 189291402Szbb 190332154Skevans od = (struct open_disk *)dev->dd.d_opendata; 191332154Skevans ret = dev->dd.d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset, 192291402Szbb blocks * od->sectorsize, buf, NULL); 193291402Szbb 194291402Szbb return (ret); 195291402Szbb} 196291402Szbb 197291402Szbbint 198329099Skevansdisk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data) 199291402Szbb{ 200332154Skevans struct open_disk *od = dev->dd.d_opendata; 201291402Szbb 202329099Skevans if (od == NULL) 203329099Skevans return (ENOTTY); 204291402Szbb 205329099Skevans switch (cmd) { 206329099Skevans case DIOCGSECTORSIZE: 207329099Skevans *(u_int *)data = od->sectorsize; 208329099Skevans break; 209329099Skevans case DIOCGMEDIASIZE: 210329099Skevans if (dev->d_offset == 0) 211329099Skevans *(uint64_t *)data = od->mediasize; 212329099Skevans else 213329099Skevans *(uint64_t *)data = od->entrysize * od->sectorsize; 214329099Skevans break; 215329099Skevans default: 216329099Skevans return (ENOTTY); 217329099Skevans } 218329099Skevans 219329099Skevans return (0); 220291402Szbb} 221291402Szbb 222291402Szbbint 223329099Skevansdisk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize) 224223695Sdfr{ 225344295Skevans struct disk_devdesc partdev; 226239058Sae struct open_disk *od; 227239058Sae struct ptable *table; 228239058Sae struct ptable_entry part; 229241053Sae int rc, slice, partition; 230223695Sdfr 231347182Stsoome if (sectorsize == 0) { 232347182Stsoome DPRINTF("unknown sector size"); 233347182Stsoome return (ENXIO); 234347182Stsoome } 235241809Sae rc = 0; 236329099Skevans od = (struct open_disk *)malloc(sizeof(struct open_disk)); 237329099Skevans if (od == NULL) { 238346483Skevans DPRINTF("no memory"); 239329099Skevans return (ENOMEM); 240241053Sae } 241332154Skevans dev->dd.d_opendata = od; 242329099Skevans od->entrysize = 0; 243239058Sae od->mediasize = mediasize; 244239058Sae od->sectorsize = sectorsize; 245344295Skevans /* 246344295Skevans * While we are reading disk metadata, make sure we do it relative 247344295Skevans * to the start of the disk 248344295Skevans */ 249344295Skevans memcpy(&partdev, dev, sizeof(partdev)); 250344295Skevans partdev.d_offset = 0; 251344295Skevans partdev.d_slice = -1; 252344295Skevans partdev.d_partition = -1; 253344295Skevans 254344295Skevans dev->d_offset = 0; 255344295Skevans table = NULL; 256344295Skevans slice = dev->d_slice; 257344295Skevans partition = dev->d_partition; 258344295Skevans 259346483Skevans DPRINTF("%s unit %d, slice %d, partition %d => %p", 260344295Skevans disk_fmtdev(dev), dev->dd.d_unit, dev->d_slice, dev->d_partition, od); 261223695Sdfr 262239058Sae /* Determine disk layout. */ 263344295Skevans od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize, 264239058Sae ptblread); 265239058Sae if (od->table == NULL) { 266346483Skevans DPRINTF("Can't read partition table"); 267239058Sae rc = ENXIO; 268223695Sdfr goto out; 269223695Sdfr } 270329099Skevans 271329099Skevans if (ptable_getsize(od->table, &mediasize) != 0) { 272329099Skevans rc = ENXIO; 273329099Skevans goto out; 274329099Skevans } 275346475Skevans od->mediasize = mediasize; 276329099Skevans 277239230Sae if (ptable_gettype(od->table) == PTABLE_BSD && 278241053Sae partition >= 0) { 279239230Sae /* It doesn't matter what value has d_slice */ 280241053Sae rc = ptable_getpart(od->table, &part, partition); 281329099Skevans if (rc == 0) { 282239230Sae dev->d_offset = part.start; 283329099Skevans od->entrysize = part.end - part.start + 1; 284329099Skevans } 285332956Sbenno } else if (ptable_gettype(od->table) == PTABLE_ISO9660) { 286332956Sbenno dev->d_offset = 0; 287332956Sbenno od->entrysize = mediasize; 288241053Sae } else if (slice >= 0) { 289239058Sae /* Try to get information about partition */ 290241053Sae if (slice == 0) 291241023Sae rc = ptable_getbestpart(od->table, &part); 292241023Sae else 293241053Sae rc = ptable_getpart(od->table, &part, slice); 294239058Sae if (rc != 0) /* Partition doesn't exist */ 295223695Sdfr goto out; 296239058Sae dev->d_offset = part.start; 297329099Skevans od->entrysize = part.end - part.start + 1; 298241053Sae slice = part.index; 299241053Sae if (ptable_gettype(od->table) == PTABLE_GPT) { 300241053Sae partition = 255; 301241053Sae goto out; /* Nothing more to do */ 302241876Sae } else if (partition == 255) { 303241876Sae /* 304241876Sae * When we try to open GPT partition, but partition 305241876Sae * table isn't GPT, reset d_partition value to -1 306241876Sae * and try to autodetect appropriate value. 307241876Sae */ 308241876Sae partition = -1; 309241023Sae } 310239293Sae /* 311239293Sae * If d_partition < 0 and we are looking at a BSD slice, 312239293Sae * then try to read BSD label, otherwise return the 313239293Sae * whole MBR slice. 314239293Sae */ 315241053Sae if (partition == -1 && 316239293Sae part.type != PART_FREEBSD) 317239293Sae goto out; 318239058Sae /* Try to read BSD label */ 319239058Sae table = ptable_open(dev, part.end - part.start + 1, 320239058Sae od->sectorsize, ptblread); 321239058Sae if (table == NULL) { 322346483Skevans DPRINTF("Can't read BSD label"); 323239058Sae rc = ENXIO; 324223695Sdfr goto out; 325223695Sdfr } 326239293Sae /* 327239293Sae * If slice contains BSD label and d_partition < 0, then 328239293Sae * assume the 'a' partition. Otherwise just return the 329239293Sae * whole MBR slice, because it can contain ZFS. 330239293Sae */ 331241053Sae if (partition < 0) { 332239293Sae if (ptable_gettype(table) != PTABLE_BSD) 333239293Sae goto out; 334241053Sae partition = 0; 335239293Sae } 336241053Sae rc = ptable_getpart(table, &part, partition); 337239058Sae if (rc != 0) 338239058Sae goto out; 339239058Sae dev->d_offset += part.start; 340329099Skevans od->entrysize = part.end - part.start + 1; 341223695Sdfr } 342223695Sdfrout: 343239058Sae if (table != NULL) 344239058Sae ptable_close(table); 345239210Sae 346239058Sae if (rc != 0) { 347329099Skevans if (od->table != NULL) 348329099Skevans ptable_close(od->table); 349329099Skevans free(od); 350346483Skevans DPRINTF("%s could not open", disk_fmtdev(dev)); 351239210Sae } else { 352241053Sae /* Save the slice and partition number to the dev */ 353241053Sae dev->d_slice = slice; 354241053Sae dev->d_partition = partition; 355346483Skevans DPRINTF("%s offset %lld => %p", disk_fmtdev(dev), 356272557Sae (long long)dev->d_offset, od); 357239058Sae } 358223695Sdfr return (rc); 359223695Sdfr} 360223695Sdfr 361239058Saeint 362239058Saedisk_close(struct disk_devdesc *dev) 363223695Sdfr{ 364239058Sae struct open_disk *od; 365223695Sdfr 366332154Skevans od = (struct open_disk *)dev->dd.d_opendata; 367346483Skevans DPRINTF("%s closed => %p", disk_fmtdev(dev), od); 368329099Skevans ptable_close(od->table); 369329099Skevans free(od); 370223695Sdfr return (0); 371223695Sdfr} 372223695Sdfr 373239058Saechar* 374239058Saedisk_fmtdev(struct disk_devdesc *dev) 375223695Sdfr{ 376239058Sae static char buf[128]; 377239058Sae char *cp; 378223695Sdfr 379332154Skevans cp = buf + sprintf(buf, "%s%d", dev->dd.d_dev->dv_name, dev->dd.d_unit); 380241053Sae if (dev->d_slice >= 0) { 381223695Sdfr#ifdef LOADER_GPT_SUPPORT 382239058Sae if (dev->d_partition == 255) { 383239058Sae sprintf(cp, "p%d:", dev->d_slice); 384239058Sae return (buf); 385239058Sae } else 386223695Sdfr#endif 387223712Smarius#ifdef LOADER_MBR_SUPPORT 388239058Sae cp += sprintf(cp, "s%d", dev->d_slice); 389223712Smarius#endif 390239058Sae } 391239230Sae if (dev->d_partition >= 0) 392239230Sae cp += sprintf(cp, "%c", dev->d_partition + 'a'); 393239058Sae strcat(cp, ":"); 394239058Sae return (buf); 395223695Sdfr} 396223695Sdfr 397239058Saeint 398239058Saedisk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path) 399223695Sdfr{ 400239058Sae int unit, slice, partition; 401239058Sae const char *np; 402239058Sae char *cp; 403223695Sdfr 404239058Sae np = devspec; 405239058Sae unit = slice = partition = -1; 406239058Sae if (*np != '\0' && *np != ':') { 407239058Sae unit = strtol(np, &cp, 10); 408239058Sae if (cp == np) 409239058Sae return (EUNIT); 410223695Sdfr#ifdef LOADER_GPT_SUPPORT 411239058Sae if (*cp == 'p') { 412239058Sae np = cp + 1; 413239058Sae slice = strtol(np, &cp, 10); 414239058Sae if (np == cp) 415239058Sae return (ESLICE); 416239058Sae /* we don't support nested partitions on GPT */ 417239058Sae if (*cp != '\0' && *cp != ':') 418239058Sae return (EINVAL); 419239058Sae partition = 255; 420239058Sae } else 421223695Sdfr#endif 422223712Smarius#ifdef LOADER_MBR_SUPPORT 423239058Sae if (*cp == 's') { 424239058Sae np = cp + 1; 425239058Sae slice = strtol(np, &cp, 10); 426239058Sae if (np == cp) 427239058Sae return (ESLICE); 428239058Sae } 429223712Smarius#endif 430239058Sae if (*cp != '\0' && *cp != ':') { 431239058Sae partition = *cp - 'a'; 432239058Sae if (partition < 0) 433239058Sae return (EPART); 434239058Sae cp++; 435239058Sae } 436239058Sae } else 437239058Sae return (EINVAL); 438239058Sae 439239058Sae if (*cp != '\0' && *cp != ':') 440239058Sae return (EINVAL); 441332154Skevans dev->dd.d_unit = unit; 442239058Sae dev->d_slice = slice; 443239058Sae dev->d_partition = partition; 444239058Sae if (path != NULL) 445239058Sae *path = (*cp == '\0') ? cp: cp + 1; 446239058Sae return (0); 447223695Sdfr} 448