disk.c revision 239210
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 239210 2012-08-12 14:16:21Z 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 (dev->d_slice > 0) { 177 /* Try to get information about partition */ 178 rc = ptable_getpart(od->table, &part, dev->d_slice); 179 if (rc != 0) /* Partition doesn't exist */ 180 goto out; 181 dev->d_offset = part.start; 182 if (dev->d_partition == -1 || 183 dev->d_partition == 255) 184 goto out; /* Nothing more to do */ 185 186 /* Try to read BSD label */ 187 table = ptable_open(dev, part.end - part.start + 1, 188 od->sectorsize, ptblread); 189 if (table == NULL) { 190 DEBUG("Can't read BSD label"); 191 rc = ENXIO; 192 goto out; 193 } 194 rc = ptable_getpart(table, &part, dev->d_partition); 195 if (rc != 0) 196 goto out; 197 dev->d_offset += part.start; 198 } else if (dev->d_slice == 0) { 199 rc = ptable_getbestpart(od->table, &part); 200 if (rc != 0) 201 goto out; 202 /* Save the slice number of best partition to dev */ 203 dev->d_slice = part.index; 204 dev->d_offset = part.start; 205 } 206out: 207 if (table != NULL) 208 ptable_close(table); 209 210 if (rc != 0) { 211 if (od->table != NULL) 212 ptable_close(od->table); 213 free(od); 214 DEBUG("%s: could not open", disk_fmtdev(dev)); 215 } else { 216 DEBUG("%s: offset %lld", disk_fmtdev(dev), dev->d_offset); 217 } 218 return (rc); 219} 220 221int 222disk_close(struct disk_devdesc *dev) 223{ 224 struct open_disk *od; 225 226 od = (struct open_disk *)dev->d_opendata; 227 DEBUG("%s: closed", disk_fmtdev(dev)); 228 ptable_close(od->table); 229 free(od); 230 return (0); 231} 232 233char* 234disk_fmtdev(struct disk_devdesc *dev) 235{ 236 static char buf[128]; 237 char *cp; 238 239 cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit); 240 if (dev->d_slice > 0) { 241#ifdef LOADER_GPT_SUPPORT 242 if (dev->d_partition == 255) { 243 sprintf(cp, "p%d:", dev->d_slice); 244 return (buf); 245 } else 246#endif 247#ifdef LOADER_MBR_SUPPORT 248 cp += sprintf(cp, "s%d", dev->d_slice); 249#endif 250 if (dev->d_partition >= 0) 251 cp += sprintf(cp, "%c", dev->d_partition + 'a'); 252 } 253 strcat(cp, ":"); 254 return (buf); 255} 256 257int 258disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path) 259{ 260 int unit, slice, partition; 261 const char *np; 262 char *cp; 263 264 np = devspec; 265 unit = slice = partition = -1; 266 if (*np != '\0' && *np != ':') { 267 unit = strtol(np, &cp, 10); 268 if (cp == np) 269 return (EUNIT); 270#ifdef LOADER_GPT_SUPPORT 271 if (*cp == 'p') { 272 np = cp + 1; 273 slice = strtol(np, &cp, 10); 274 if (np == cp) 275 return (ESLICE); 276 /* we don't support nested partitions on GPT */ 277 if (*cp != '\0' && *cp != ':') 278 return (EINVAL); 279 partition = 255; 280 } else 281#endif 282#ifdef LOADER_MBR_SUPPORT 283 if (*cp == 's') { 284 np = cp + 1; 285 slice = strtol(np, &cp, 10); 286 if (np == cp) 287 return (ESLICE); 288 } 289#endif 290 if (*cp != '\0' && *cp != ':') { 291 partition = *cp - 'a'; 292 if (partition < 0) 293 return (EPART); 294 cp++; 295 } 296 } else 297 return (EINVAL); 298 299 if (*cp != '\0' && *cp != ':') 300 return (EINVAL); 301 dev->d_unit = unit; 302 dev->d_slice = slice; 303 dev->d_partition = partition; 304 if (path != NULL) 305 *path = (*cp == '\0') ? cp: cp + 1; 306 return (0); 307} 308