disk.c revision 241023
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 241023 2012-09-28 10:49:41Z ae $"); 30 31#include <sys/disk.h> 32#include <stand.h> 33#include <stdarg.h> 34#include <bootstrap.h> 35#include <part.h> 36 37#include "disk.h" 38 39#ifdef DISK_DEBUG 40# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 41#else 42# define DEBUG(fmt, args...) 43#endif 44 45struct open_disk { 46 struct ptable *table; 47 off_t mediasize; 48 u_int sectorsize; 49}; 50 51struct print_args { 52 struct disk_devdesc *dev; 53 const char *prefix; 54 int verbose; 55}; 56 57/* Convert size to a human-readable number. */ 58static char * 59display_size(uint64_t size, u_int sectorsize) 60{ 61 static char buf[80]; 62 char unit; 63 64 size = size * sectorsize / 1024; 65 unit = 'K'; 66 if (size >= 10485760000LL) { 67 size /= 1073741824; 68 unit = 'T'; 69 } else if (size >= 10240000) { 70 size /= 1048576; 71 unit = 'G'; 72 } else if (size >= 10000) { 73 size /= 1024; 74 unit = 'M'; 75 } 76 sprintf(buf, "%ld%cB", (long)size, unit); 77 return (buf); 78} 79 80static int 81ptblread(void *d, void *buf, size_t blocks, off_t offset) 82{ 83 struct disk_devdesc *dev; 84 struct open_disk *od; 85 86 dev = (struct disk_devdesc *)d; 87 od = (struct open_disk *)dev->d_opendata; 88 return (dev->d_dev->dv_strategy(dev, F_READ, offset, 89 blocks * od->sectorsize, (char *)buf, NULL)); 90} 91 92#define PWIDTH 35 93static void 94ptable_print(void *arg, const char *pname, const struct ptable_entry *part) 95{ 96 struct print_args *pa, bsd; 97 struct open_disk *od; 98 struct ptable *table; 99 char line[80]; 100 101 pa = (struct print_args *)arg; 102 od = (struct open_disk *)pa->dev->d_opendata; 103 sprintf(line, " %s%s: %s", pa->prefix, pname, 104 parttype2str(part->type)); 105 if (pa->verbose) 106 sprintf(line, "%-*s%s", PWIDTH, line, 107 display_size(part->end - part->start + 1, 108 od->sectorsize)); 109 strcat(line, "\n"); 110 pager_output(line); 111 if (part->type == PART_FREEBSD) { 112 /* Open slice with BSD label */ 113 pa->dev->d_offset = part->start; 114 table = ptable_open(pa->dev, part->end - part->start + 1, 115 od->sectorsize, ptblread); 116 if (table == NULL) 117 return; 118 sprintf(line, " %s%s", pa->prefix, pname); 119 bsd.dev = pa->dev; 120 bsd.prefix = line; 121 bsd.verbose = pa->verbose; 122 ptable_iterate(table, &bsd, ptable_print); 123 ptable_close(table); 124 } 125} 126#undef PWIDTH 127 128void 129disk_print(struct disk_devdesc *dev, char *prefix, int verbose) 130{ 131 struct open_disk *od; 132 struct print_args pa; 133 134 /* Disk should be opened */ 135 od = (struct open_disk *)dev->d_opendata; 136 pa.dev = dev; 137 pa.prefix = prefix; 138 pa.verbose = verbose; 139 ptable_iterate(od->table, &pa, ptable_print); 140} 141 142int 143disk_open(struct disk_devdesc *dev, off_t mediasize, u_int sectorsize) 144{ 145 struct open_disk *od; 146 struct ptable *table; 147 struct ptable_entry part; 148 int rc; 149 150 od = (struct open_disk *)malloc(sizeof(struct open_disk)); 151 if (od == NULL) { 152 DEBUG("no memory"); 153 return (ENOMEM); 154 } 155 /* 156 * While we are reading disk metadata, make sure we do it relative 157 * to the start of the disk 158 */ 159 rc = 0; 160 table = NULL; 161 dev->d_offset = 0; 162 dev->d_opendata = od; 163 od->mediasize = mediasize; 164 od->sectorsize = sectorsize; 165 DEBUG("%s unit %d, slice %d, partition %d", 166 disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition); 167 168 /* Determine disk layout. */ 169 od->table = ptable_open(dev, mediasize / sectorsize, sectorsize, 170 ptblread); 171 if (od->table == NULL) { 172 DEBUG("Can't read partition table"); 173 rc = ENXIO; 174 goto out; 175 } 176 if (ptable_gettype(od->table) == PTABLE_BSD && 177 dev->d_partition >= 0) { 178 /* It doesn't matter what value has d_slice */ 179 rc = ptable_getpart(od->table, &part, dev->d_partition); 180 if (rc == 0) 181 dev->d_offset = part.start; 182 } else if (dev->d_slice >= 0) { 183 /* Try to get information about partition */ 184 if (dev->d_slice == 0) 185 rc = ptable_getbestpart(od->table, &part); 186 else 187 rc = ptable_getpart(od->table, &part, dev->d_slice); 188 if (rc != 0) /* Partition doesn't exist */ 189 goto out; 190 dev->d_offset = part.start; 191 if (dev->d_slice == 0) { 192 /* Save the slice number of best partition to dev */ 193 dev->d_slice = part.index; 194 if (ptable_gettype(od->table) == PTABLE_GPT) 195 dev->d_partition = 255; 196 } 197 if (dev->d_partition == 255) 198 goto out; /* Nothing more to do */ 199 /* 200 * If d_partition < 0 and we are looking at a BSD slice, 201 * then try to read BSD label, otherwise return the 202 * whole MBR slice. 203 */ 204 if (dev->d_partition == -1 && 205 part.type != PART_FREEBSD) 206 goto out; 207 /* Try to read BSD label */ 208 table = ptable_open(dev, part.end - part.start + 1, 209 od->sectorsize, ptblread); 210 if (table == NULL) { 211 DEBUG("Can't read BSD label"); 212 rc = ENXIO; 213 goto out; 214 } 215 /* 216 * If slice contains BSD label and d_partition < 0, then 217 * assume the 'a' partition. Otherwise just return the 218 * whole MBR slice, because it can contain ZFS. 219 */ 220 if (dev->d_partition < 0) { 221 if (ptable_gettype(table) != PTABLE_BSD) 222 goto out; 223 dev->d_partition = 0; 224 } 225 rc = ptable_getpart(table, &part, dev->d_partition); 226 if (rc != 0) 227 goto out; 228 dev->d_offset += part.start; 229 } 230out: 231 if (table != NULL) 232 ptable_close(table); 233 234 if (rc != 0) { 235 if (od->table != NULL) 236 ptable_close(od->table); 237 free(od); 238 DEBUG("%s could not open", disk_fmtdev(dev)); 239 } else { 240 DEBUG("%s offset %lld", disk_fmtdev(dev), dev->d_offset); 241 } 242 return (rc); 243} 244 245int 246disk_close(struct disk_devdesc *dev) 247{ 248 struct open_disk *od; 249 250 od = (struct open_disk *)dev->d_opendata; 251 DEBUG("%s closed", disk_fmtdev(dev)); 252 ptable_close(od->table); 253 free(od); 254 return (0); 255} 256 257char* 258disk_fmtdev(struct disk_devdesc *dev) 259{ 260 static char buf[128]; 261 char *cp; 262 263 cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit); 264 if (dev->d_slice > 0) { 265#ifdef LOADER_GPT_SUPPORT 266 if (dev->d_partition == 255) { 267 sprintf(cp, "p%d:", dev->d_slice); 268 return (buf); 269 } else 270#endif 271#ifdef LOADER_MBR_SUPPORT 272 cp += sprintf(cp, "s%d", dev->d_slice); 273#endif 274 } 275 if (dev->d_partition >= 0) 276 cp += sprintf(cp, "%c", dev->d_partition + 'a'); 277 strcat(cp, ":"); 278 return (buf); 279} 280 281int 282disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path) 283{ 284 int unit, slice, partition; 285 const char *np; 286 char *cp; 287 288 np = devspec; 289 unit = slice = partition = -1; 290 if (*np != '\0' && *np != ':') { 291 unit = strtol(np, &cp, 10); 292 if (cp == np) 293 return (EUNIT); 294#ifdef LOADER_GPT_SUPPORT 295 if (*cp == 'p') { 296 np = cp + 1; 297 slice = strtol(np, &cp, 10); 298 if (np == cp) 299 return (ESLICE); 300 /* we don't support nested partitions on GPT */ 301 if (*cp != '\0' && *cp != ':') 302 return (EINVAL); 303 partition = 255; 304 } else 305#endif 306#ifdef LOADER_MBR_SUPPORT 307 if (*cp == 's') { 308 np = cp + 1; 309 slice = strtol(np, &cp, 10); 310 if (np == cp) 311 return (ESLICE); 312 } 313#endif 314 if (*cp != '\0' && *cp != ':') { 315 partition = *cp - 'a'; 316 if (partition < 0) 317 return (EPART); 318 cp++; 319 } 320 } else 321 return (EINVAL); 322 323 if (*cp != '\0' && *cp != ':') 324 return (EINVAL); 325 dev->d_unit = unit; 326 dev->d_slice = slice; 327 dev->d_partition = partition; 328 if (path != NULL) 329 *path = (*cp == '\0') ? cp: cp + 1; 330 return (0); 331} 332