]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/common/disk.c
MFC Loader Fixes 2017q1: r311458,r312237,r312314,r312374,r312947,r313042,
[FreeBSD/FreeBSD.git] / sys / boot / common / disk.c
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$");
30
31 #include <sys/disk.h>
32 #include <sys/queue.h>
33 #include <stand.h>
34 #include <stdarg.h>
35 #include <bootstrap.h>
36 #include <part.h>
37
38 #include "disk.h"
39
40 #ifdef DISK_DEBUG
41 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
42 #else
43 # define DEBUG(fmt, args...)
44 #endif
45
46 struct open_disk {
47         struct ptable           *table;
48         uint64_t                mediasize;
49         uint64_t                entrysize;
50         u_int                   sectorsize;
51 };
52
53 struct print_args {
54         struct disk_devdesc     *dev;
55         const char              *prefix;
56         int                     verbose;
57 };
58
59 /* Convert size to a human-readable number. */
60 static char *
61 display_size(uint64_t size, u_int sectorsize)
62 {
63         static char buf[80];
64         char unit;
65
66         size = size * sectorsize / 1024;
67         unit = 'K';
68         if (size >= 10485760000LL) {
69                 size /= 1073741824;
70                 unit = 'T';
71         } else if (size >= 10240000) {
72                 size /= 1048576;
73                 unit = 'G';
74         } else if (size >= 10000) {
75                 size /= 1024;
76                 unit = 'M';
77         }
78         sprintf(buf, "%ld%cB", (long)size, unit);
79         return (buf);
80 }
81
82 int
83 ptblread(void *d, void *buf, size_t blocks, uint64_t offset)
84 {
85         struct disk_devdesc *dev;
86         struct open_disk *od;
87
88         dev = (struct disk_devdesc *)d;
89         od = (struct open_disk *)dev->d_opendata;
90         return (dev->d_dev->dv_strategy(dev, F_READ, offset,
91             blocks * od->sectorsize, (char *)buf, NULL));
92 }
93
94 #define PWIDTH  35
95 static int
96 ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
97 {
98         struct disk_devdesc dev;
99         struct print_args *pa, bsd;
100         struct open_disk *od;
101         struct ptable *table;
102         char line[80];
103         int res;
104
105         pa = (struct print_args *)arg;
106         od = (struct open_disk *)pa->dev->d_opendata;
107         sprintf(line, "  %s%s: %s", pa->prefix, pname,
108             parttype2str(part->type));
109         if (pa->verbose)
110                 sprintf(line, "%-*s%s", PWIDTH, line,
111                     display_size(part->end - part->start + 1,
112                     od->sectorsize));
113         strcat(line, "\n");
114         if (pager_output(line))
115                 return 1;
116         res = 0;
117         if (part->type == PART_FREEBSD) {
118                 /* Open slice with BSD label */
119                 dev.d_dev = pa->dev->d_dev;
120                 dev.d_unit = pa->dev->d_unit;
121                 dev.d_slice = part->index;
122                 dev.d_partition = -1;
123                 if (disk_open(&dev, part->end - part->start + 1,
124                     od->sectorsize) == 0) {
125                         table = ptable_open(&dev, part->end - part->start + 1,
126                             od->sectorsize, ptblread);
127                         if (table != NULL) {
128                                 sprintf(line, "  %s%s", pa->prefix, pname);
129                                 bsd.dev = pa->dev;
130                                 bsd.prefix = line;
131                                 bsd.verbose = pa->verbose;
132                                 res = ptable_iterate(table, &bsd, ptable_print);
133                                 ptable_close(table);
134                         }
135                         disk_close(&dev);
136                 }
137         }
138
139         return (res);
140 }
141 #undef PWIDTH
142
143 int
144 disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
145 {
146         struct open_disk *od;
147         struct print_args pa;
148
149         /* Disk should be opened */
150         od = (struct open_disk *)dev->d_opendata;
151         pa.dev = dev;
152         pa.prefix = prefix;
153         pa.verbose = verbose;
154         return (ptable_iterate(od->table, &pa, ptable_print));
155 }
156
157 int
158 disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
159 {
160         struct open_disk *od;
161         int ret;
162
163         od = (struct open_disk *)dev->d_opendata;
164         ret = dev->d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset,
165             blocks * od->sectorsize, buf, NULL);
166
167         return (ret);
168 }
169
170 int
171 disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
172 {
173         struct open_disk *od;
174         int ret;
175
176         od = (struct open_disk *)dev->d_opendata;
177         ret = dev->d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset,
178             blocks * od->sectorsize, buf, NULL);
179
180         return (ret);
181 }
182
183 int
184 disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
185 {
186         struct open_disk *od = dev->d_opendata;
187
188         if (od == NULL)
189                 return (ENOTTY);
190
191         switch (cmd) {
192         case DIOCGSECTORSIZE:
193                 *(u_int *)data = od->sectorsize;
194                 break;
195         case DIOCGMEDIASIZE:
196                 if (dev->d_offset == 0)
197                         *(uint64_t *)data = od->mediasize;
198                 else
199                         *(uint64_t *)data = od->entrysize * od->sectorsize;
200                 break;
201         default:
202                 return (ENOTTY);
203         }
204
205         return (0);
206 }
207
208 int
209 disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize)
210 {
211         struct open_disk *od;
212         struct ptable *table;
213         struct ptable_entry part;
214         int rc, slice, partition;
215
216         rc = 0;
217         /*
218          * While we are reading disk metadata, make sure we do it relative
219          * to the start of the disk
220          */
221         dev->d_offset = 0;
222         table = NULL;
223         slice = dev->d_slice;
224         partition = dev->d_partition;
225         od = (struct open_disk *)malloc(sizeof(struct open_disk));
226         if (od == NULL) {
227                 DEBUG("no memory");
228                 return (ENOMEM);
229         }
230         dev->d_opendata = od;
231         od->entrysize = 0;
232         od->mediasize = mediasize;
233         od->sectorsize = sectorsize;
234         DEBUG("%s unit %d, slice %d, partition %d => %p",
235             disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition, od);
236
237         /* Determine disk layout. */
238         od->table = ptable_open(dev, mediasize / sectorsize, sectorsize,
239             ptblread);
240         if (od->table == NULL) {
241                 DEBUG("Can't read partition table");
242                 rc = ENXIO;
243                 goto out;
244         }
245
246         if (ptable_getsize(od->table, &mediasize) != 0) {
247                 rc = ENXIO;
248                 goto out;
249         }
250         if (mediasize > od->mediasize) {
251                 od->mediasize = mediasize;
252         }
253
254         if (ptable_gettype(od->table) == PTABLE_BSD &&
255             partition >= 0) {
256                 /* It doesn't matter what value has d_slice */
257                 rc = ptable_getpart(od->table, &part, partition);
258                 if (rc == 0) {
259                         dev->d_offset = part.start;
260                         od->entrysize = part.end - part.start + 1;
261                 }
262         } else if (slice >= 0) {
263                 /* Try to get information about partition */
264                 if (slice == 0)
265                         rc = ptable_getbestpart(od->table, &part);
266                 else
267                         rc = ptable_getpart(od->table, &part, slice);
268                 if (rc != 0) /* Partition doesn't exist */
269                         goto out;
270                 dev->d_offset = part.start;
271                 od->entrysize = part.end - part.start + 1;
272                 slice = part.index;
273                 if (ptable_gettype(od->table) == PTABLE_GPT) {
274                         partition = 255;
275                         goto out; /* Nothing more to do */
276                 } else if (partition == 255) {
277                         /*
278                          * When we try to open GPT partition, but partition
279                          * table isn't GPT, reset d_partition value to -1
280                          * and try to autodetect appropriate value.
281                          */
282                         partition = -1;
283                 }
284                 /*
285                  * If d_partition < 0 and we are looking at a BSD slice,
286                  * then try to read BSD label, otherwise return the
287                  * whole MBR slice.
288                  */
289                 if (partition == -1 &&
290                     part.type != PART_FREEBSD)
291                         goto out;
292                 /* Try to read BSD label */
293                 table = ptable_open(dev, part.end - part.start + 1,
294                     od->sectorsize, ptblread);
295                 if (table == NULL) {
296                         DEBUG("Can't read BSD label");
297                         rc = ENXIO;
298                         goto out;
299                 }
300                 /*
301                  * If slice contains BSD label and d_partition < 0, then
302                  * assume the 'a' partition. Otherwise just return the
303                  * whole MBR slice, because it can contain ZFS.
304                  */
305                 if (partition < 0) {
306                         if (ptable_gettype(table) != PTABLE_BSD)
307                                 goto out;
308                         partition = 0;
309                 }
310                 rc = ptable_getpart(table, &part, partition);
311                 if (rc != 0)
312                         goto out;
313                 dev->d_offset += part.start;
314                 od->entrysize = part.end - part.start + 1;
315         }
316 out:
317         if (table != NULL)
318                 ptable_close(table);
319
320         if (rc != 0) {
321                 if (od->table != NULL)
322                         ptable_close(od->table);
323                 free(od);
324                 DEBUG("%s could not open", disk_fmtdev(dev));
325         } else {
326                 /* Save the slice and partition number to the dev */
327                 dev->d_slice = slice;
328                 dev->d_partition = partition;
329                 DEBUG("%s offset %lld => %p", disk_fmtdev(dev),
330                     (long long)dev->d_offset, od);
331         }
332         return (rc);
333 }
334
335 int
336 disk_close(struct disk_devdesc *dev)
337 {
338         struct open_disk *od;
339
340         od = (struct open_disk *)dev->d_opendata;
341         DEBUG("%s closed => %p", disk_fmtdev(dev), od);
342         ptable_close(od->table);
343         free(od);
344         return (0);
345 }
346
347 char*
348 disk_fmtdev(struct disk_devdesc *dev)
349 {
350         static char buf[128];
351         char *cp;
352
353         cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit);
354         if (dev->d_slice >= 0) {
355 #ifdef LOADER_GPT_SUPPORT
356                 if (dev->d_partition == 255) {
357                         sprintf(cp, "p%d:", dev->d_slice);
358                         return (buf);
359                 } else
360 #endif
361 #ifdef LOADER_MBR_SUPPORT
362                         cp += sprintf(cp, "s%d", dev->d_slice);
363 #endif
364         }
365         if (dev->d_partition >= 0)
366                 cp += sprintf(cp, "%c", dev->d_partition + 'a');
367         strcat(cp, ":");
368         return (buf);
369 }
370
371 int
372 disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
373 {
374         int unit, slice, partition;
375         const char *np;
376         char *cp;
377
378         np = devspec;
379         unit = slice = partition = -1;
380         if (*np != '\0' && *np != ':') {
381                 unit = strtol(np, &cp, 10);
382                 if (cp == np)
383                         return (EUNIT);
384 #ifdef LOADER_GPT_SUPPORT
385                 if (*cp == 'p') {
386                         np = cp + 1;
387                         slice = strtol(np, &cp, 10);
388                         if (np == cp)
389                                 return (ESLICE);
390                         /* we don't support nested partitions on GPT */
391                         if (*cp != '\0' && *cp != ':')
392                                 return (EINVAL);
393                         partition = 255;
394                 } else
395 #endif
396 #ifdef LOADER_MBR_SUPPORT
397                 if (*cp == 's') {
398                         np = cp + 1;
399                         slice = strtol(np, &cp, 10);
400                         if (np == cp)
401                                 return (ESLICE);
402                 }
403 #endif
404                 if (*cp != '\0' && *cp != ':') {
405                         partition = *cp - 'a';
406                         if (partition < 0)
407                                 return (EPART);
408                         cp++;
409                 }
410         } else
411                 return (EINVAL);
412
413         if (*cp != '\0' && *cp != ':')
414                 return (EINVAL);
415         dev->d_unit = unit;
416         dev->d_slice = slice;
417         dev->d_partition = partition;
418         if (path != NULL)
419                 *path = (*cp == '\0') ? cp: cp + 1;
420         return (0);
421 }