2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
30 #include <sys/queue.h>
34 #include <bootstrap.h>
40 #define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
42 #define DEBUG(fmt, args...)
53 struct disk_devdesc
*dev
;
58 /* Convert size to a human-readable number. */
60 display_size(uint64_t size
, uint_t sectorsize
)
65 size
= size
* sectorsize
/ 1024;
67 if (size
>= 10485760000LL) {
70 } else if (size
>= 10240000) {
73 } else if (size
>= 10000) {
77 snprintf(buf
, sizeof (buf
), "%" PRIu64
"%cB", size
, unit
);
82 ptblread(void *d
, void *buf
, size_t blocks
, uint64_t offset
)
84 struct disk_devdesc
*dev
;
87 dev
= (struct disk_devdesc
*)d
;
88 od
= (struct open_disk
*)dev
->dd
.d_opendata
;
91 * The strategy function assumes the offset is in units of 512 byte
92 * sectors. For larger sector sizes, we need to adjust the offset to
93 * match the actual sector size.
95 offset
*= (od
->sectorsize
/ 512);
97 * As the GPT backup partition is located at the end of the disk,
98 * to avoid reading past disk end, flag bcache not to use RA.
100 return (dev
->dd
.d_dev
->dv_strategy(dev
, F_READ
| F_NORA
, offset
,
101 blocks
* od
->sectorsize
, (char *)buf
, NULL
));
106 ptable_print(void *arg
, const char *pname
, const struct ptable_entry
*part
)
108 struct disk_devdesc dev
;
109 struct print_args
*pa
, bsd
;
110 struct open_disk
*od
;
111 struct ptable
*table
;
115 pa
= (struct print_args
*)arg
;
116 od
= (struct open_disk
*)pa
->dev
->dd
.d_opendata
;
117 sprintf(line
, " %s%s: %s", pa
->prefix
, pname
,
118 parttype2str(part
->type
));
120 sprintf(line
, "%-*s%s", PWIDTH
, line
,
121 display_size(part
->end
- part
->start
+ 1,
124 ret
= pager_output(line
);
127 if (part
->type
== PART_FREEBSD
|| part
->type
== PART_SOLARIS2
) {
128 /* Open slice with BSD or VTOC label */
129 dev
.dd
.d_dev
= pa
->dev
->dd
.d_dev
;
130 dev
.dd
.d_unit
= pa
->dev
->dd
.d_unit
;
131 dev
.d_slice
= part
->index
;
132 dev
.d_partition
= -1;
133 if (disk_open(&dev
, part
->end
- part
->start
+ 1,
134 od
->sectorsize
) == 0) {
135 enum ptable_type pt
= PTABLE_NONE
;
137 table
= ptable_open(&dev
, part
->end
- part
->start
+ 1,
138 od
->sectorsize
, ptblread
);
140 pt
= ptable_gettype(table
);
142 if (pt
== PTABLE_BSD
||
143 pt
== PTABLE_VTOC8
||
145 sprintf(line
, " %s%s", pa
->prefix
, pname
);
148 bsd
.verbose
= pa
->verbose
;
149 ret
= ptable_iterate(table
, &bsd
, ptable_print
);
160 disk_print(struct disk_devdesc
*dev
, char *prefix
, int verbose
)
162 struct open_disk
*od
;
163 struct print_args pa
;
165 /* Disk should be opened */
166 od
= (struct open_disk
*)dev
->dd
.d_opendata
;
169 pa
.verbose
= verbose
;
170 return (ptable_iterate(od
->table
, &pa
, ptable_print
));
174 disk_read(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, uint_t blocks
)
176 struct open_disk
*od
;
179 od
= (struct open_disk
*)dev
->dd
.d_opendata
;
180 ret
= dev
->dd
.d_dev
->dv_strategy(dev
, F_READ
, dev
->d_offset
+ offset
,
181 blocks
* od
->sectorsize
, buf
, NULL
);
187 disk_write(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, uint_t blocks
)
189 struct open_disk
*od
;
192 od
= (struct open_disk
*)dev
->dd
.d_opendata
;
193 ret
= dev
->dd
.d_dev
->dv_strategy(dev
, F_WRITE
, dev
->d_offset
+ offset
,
194 blocks
* od
->sectorsize
, buf
, NULL
);
200 disk_ioctl(struct disk_devdesc
*dev
, unsigned long cmd
, void *data
)
202 struct open_disk
*od
= dev
->dd
.d_opendata
;
208 case DIOCGSECTORSIZE
:
209 *(uint_t
*)data
= od
->sectorsize
;
212 if (dev
->d_offset
== 0)
213 *(uint64_t *)data
= od
->mediasize
;
215 *(uint64_t *)data
= od
->entrysize
* od
->sectorsize
;
225 disk_open(struct disk_devdesc
*dev
, uint64_t mediasize
, uint_t sectorsize
)
227 struct open_disk
*od
;
228 struct ptable
*table
;
229 struct ptable_entry part
;
230 int rc
, slice
, partition
;
234 * While we are reading disk metadata, make sure we do it relative
235 * to the start of the disk
239 slice
= dev
->d_slice
;
240 partition
= dev
->d_partition
;
241 od
= (struct open_disk
*)malloc(sizeof (struct open_disk
));
246 dev
->dd
.d_opendata
= od
;
248 od
->mediasize
= mediasize
;
249 od
->sectorsize
= sectorsize
;
250 DEBUG("%s unit %d, slice %d, partition %d => %p", disk_fmtdev(dev
),
251 dev
->dd
.d_unit
, dev
->d_slice
, dev
->d_partition
, od
);
253 /* Determine disk layout. */
254 od
->table
= ptable_open(dev
, mediasize
/ sectorsize
, sectorsize
,
256 if (od
->table
== NULL
) {
257 DEBUG("Can't read partition table");
262 if (ptable_getsize(od
->table
, &mediasize
) != 0) {
266 od
->mediasize
= mediasize
;
268 if (ptable_gettype(od
->table
) == PTABLE_BSD
&&
270 /* It doesn't matter what value has d_slice */
271 rc
= ptable_getpart(od
->table
, &part
, partition
);
273 dev
->d_offset
= part
.start
;
274 od
->entrysize
= part
.end
- part
.start
+ 1;
276 } else if (ptable_gettype(od
->table
) == PTABLE_ISO9660
) {
278 od
->entrysize
= mediasize
;
279 } else if (slice
>= 0) {
280 /* Try to get information about partition */
282 rc
= ptable_getbestpart(od
->table
, &part
);
284 rc
= ptable_getpart(od
->table
, &part
, slice
);
285 if (rc
!= 0) /* Partition doesn't exist */
287 dev
->d_offset
= part
.start
;
288 od
->entrysize
= part
.end
- part
.start
+ 1;
290 if (ptable_gettype(od
->table
) == PTABLE_GPT
) {
292 goto out
; /* Nothing more to do */
293 } else if (partition
== 255) {
295 * When we try to open GPT partition, but partition
296 * table isn't GPT, reset d_partition value to -1
297 * and try to autodetect appropriate value.
302 * If d_partition < 0 and we are looking at a BSD/VTOC slice,
303 * then try to read label, otherwise return the
306 if (partition
== -1 &&
307 (part
.type
!= PART_FREEBSD
|| part
.type
!= PART_SOLARIS2
))
309 /* Try to read label */
310 table
= ptable_open(dev
, part
.end
- part
.start
+ 1,
311 od
->sectorsize
, ptblread
);
313 DEBUG("Can't read BSD/VTOC label");
318 * If slice contains BSD/VTOC label and d_partition < 0, then
319 * assume the 'a' partition. Otherwise just return the
320 * whole MBR slice, because it can contain ZFS.
323 if (ptable_gettype(table
) != PTABLE_BSD
||
324 ptable_gettype(table
) != PTABLE_VTOC
)
328 rc
= ptable_getpart(table
, &part
, partition
);
331 dev
->d_offset
+= part
.start
;
332 od
->entrysize
= part
.end
- part
.start
+ 1;
339 if (od
->table
!= NULL
)
340 ptable_close(od
->table
);
342 DEBUG("%s could not open", disk_fmtdev(dev
));
344 /* Save the slice and partition number to the dev */
345 dev
->d_slice
= slice
;
346 dev
->d_partition
= partition
;
347 DEBUG("%s offset %" PRIu64
" => %p", disk_fmtdev(dev
),
354 disk_close(struct disk_devdesc
*dev
)
356 struct open_disk
*od
;
358 od
= (struct open_disk
*)dev
->dd
.d_opendata
;
359 DEBUG("%s closed => %p", disk_fmtdev(dev
), od
);
360 ptable_close(od
->table
);
366 disk_fmtdev(struct disk_devdesc
*dev
)
368 static char buf
[128];
371 cp
= buf
+ sprintf(buf
, "%s%d", dev
->dd
.d_dev
->dv_name
, dev
->dd
.d_unit
);
372 if (dev
->d_slice
>= 0) {
373 #ifdef LOADER_GPT_SUPPORT
374 if (dev
->d_partition
== 255) {
375 sprintf(cp
, "p%d:", dev
->d_slice
);
379 #ifdef LOADER_MBR_SUPPORT
380 cp
+= sprintf(cp
, "s%d", dev
->d_slice
);
383 if (dev
->d_partition
>= 0)
384 cp
+= sprintf(cp
, "%c", dev
->d_partition
+ 'a');
390 disk_parsedev(struct disk_devdesc
*dev
, const char *devspec
, const char **path
)
392 int unit
, slice
, partition
;
397 unit
= slice
= partition
= -1;
398 if (*np
!= '\0' && *np
!= ':') {
399 unit
= strtol(np
, &cp
, 10);
402 #ifdef LOADER_GPT_SUPPORT
405 slice
= strtol(np
, &cp
, 10);
408 /* we don't support nested partitions on GPT */
409 if (*cp
!= '\0' && *cp
!= ':')
414 #ifdef LOADER_MBR_SUPPORT
417 slice
= strtol(np
, &cp
, 10);
422 if (*cp
!= '\0' && *cp
!= ':') {
423 partition
= *cp
- 'a';
431 if (*cp
!= '\0' && *cp
!= ':')
433 dev
->dd
.d_unit
= unit
;
434 dev
->d_slice
= slice
;
435 dev
->d_partition
= partition
;
437 *path
= (*cp
== '\0') ? cp
: cp
+ 1;