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>
33 #include <bootstrap.h>
39 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
41 # define DEBUG(fmt, args...)
52 struct disk_devdesc
*dev
;
57 /* Convert size to a human-readable number. */
59 display_size(uint64_t size
, u_int sectorsize
)
64 size
= size
* sectorsize
/ 1024;
66 if (size
>= 10485760000LL) {
69 } else if (size
>= 10240000) {
72 } else if (size
>= 10000) {
76 sprintf(buf
, "%ld%cB", (long)size
, unit
);
81 ptblread(void *d
, void *buf
, size_t blocks
, uint64_t offset
)
83 struct disk_devdesc
*dev
;
86 dev
= (struct disk_devdesc
*)d
;
87 od
= (struct open_disk
*)dev
->d_opendata
;
90 * The strategy function assumes the offset is in units of 512 byte
91 * sectors. For larger sector sizes, we need to adjust the offset to
92 * match the actual sector size.
94 offset
*= (od
->sectorsize
/ 512);
96 * As the GPT backup partition is located at the end of the disk,
97 * to avoid reading past disk end, flag bcache not to use RA.
99 return (dev
->d_dev
->dv_strategy(dev
, F_READ
| F_NORA
, offset
,
100 blocks
* od
->sectorsize
, (char *)buf
, NULL
));
105 ptable_print(void *arg
, const char *pname
, const struct ptable_entry
*part
)
107 struct disk_devdesc dev
;
108 struct print_args
*pa
, bsd
;
109 struct open_disk
*od
;
110 struct ptable
*table
;
114 pa
= (struct print_args
*)arg
;
115 od
= (struct open_disk
*)pa
->dev
->d_opendata
;
116 sprintf(line
, " %s%s: %s", pa
->prefix
, pname
,
117 parttype2str(part
->type
));
119 sprintf(line
, "%-*s%s", PWIDTH
, line
,
120 display_size(part
->end
- part
->start
+ 1,
123 ret
= pager_output(line
);
126 if (part
->type
== PART_FREEBSD
|| part
->type
== PART_SOLARIS2
) {
127 /* Open slice with BSD or VTOC label */
128 dev
.d_dev
= pa
->dev
->d_dev
;
129 dev
.d_unit
= pa
->dev
->d_unit
;
130 dev
.d_slice
= part
->index
;
131 dev
.d_partition
= -1;
132 if (disk_open(&dev
, part
->end
- part
->start
+ 1,
133 od
->sectorsize
) == 0) {
134 table
= ptable_open(&dev
, part
->end
- part
->start
+ 1,
135 od
->sectorsize
, ptblread
);
137 sprintf(line
, " %s%s", pa
->prefix
, pname
);
140 bsd
.verbose
= pa
->verbose
;
141 ret
= ptable_iterate(table
, &bsd
, ptable_print
);
152 disk_print(struct disk_devdesc
*dev
, char *prefix
, int verbose
)
154 struct open_disk
*od
;
155 struct print_args pa
;
157 /* Disk should be opened */
158 od
= (struct open_disk
*)dev
->d_opendata
;
161 pa
.verbose
= verbose
;
162 return (ptable_iterate(od
->table
, &pa
, ptable_print
));
166 disk_read(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, u_int blocks
)
168 struct open_disk
*od
;
171 od
= (struct open_disk
*)dev
->d_opendata
;
172 ret
= dev
->d_dev
->dv_strategy(dev
, F_READ
, dev
->d_offset
+ offset
,
173 blocks
* od
->sectorsize
, buf
, NULL
);
179 disk_write(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, u_int blocks
)
181 struct open_disk
*od
;
184 od
= (struct open_disk
*)dev
->d_opendata
;
185 ret
= dev
->d_dev
->dv_strategy(dev
, F_WRITE
, dev
->d_offset
+ offset
,
186 blocks
* od
->sectorsize
, buf
, NULL
);
192 disk_ioctl(struct disk_devdesc
*dev
, u_long cmd
, void *data
)
194 struct open_disk
*od
= dev
->d_opendata
;
200 case DIOCGSECTORSIZE
:
201 *(u_int
*)data
= od
->sectorsize
;
204 if (dev
->d_offset
== 0)
205 *(uint64_t *)data
= od
->mediasize
;
207 *(uint64_t *)data
= od
->entrysize
* od
->sectorsize
;
217 disk_open(struct disk_devdesc
*dev
, uint64_t mediasize
, u_int sectorsize
)
219 struct open_disk
*od
;
220 struct ptable
*table
;
221 struct ptable_entry part
;
222 int rc
, slice
, partition
;
226 * While we are reading disk metadata, make sure we do it relative
227 * to the start of the disk
231 slice
= dev
->d_slice
;
232 partition
= dev
->d_partition
;
233 od
= (struct open_disk
*)malloc(sizeof(struct open_disk
));
238 dev
->d_opendata
= od
;
240 od
->mediasize
= mediasize
;
241 od
->sectorsize
= sectorsize
;
242 DEBUG("%s unit %d, slice %d, partition %d => %p",
243 disk_fmtdev(dev
), dev
->d_unit
, dev
->d_slice
, dev
->d_partition
, od
);
245 /* Determine disk layout. */
246 od
->table
= ptable_open(dev
, mediasize
/ sectorsize
, sectorsize
,
248 if (od
->table
== NULL
) {
249 DEBUG("Can't read partition table");
254 if (ptable_getsize(od
->table
, &mediasize
) != 0) {
258 if (mediasize
> od
->mediasize
) {
259 od
->mediasize
= mediasize
;
262 if (ptable_gettype(od
->table
) == PTABLE_BSD
&&
264 /* It doesn't matter what value has d_slice */
265 rc
= ptable_getpart(od
->table
, &part
, partition
);
267 dev
->d_offset
= part
.start
;
268 od
->entrysize
= part
.end
- part
.start
+ 1;
270 } else if (slice
>= 0) {
271 /* Try to get information about partition */
273 rc
= ptable_getbestpart(od
->table
, &part
);
275 rc
= ptable_getpart(od
->table
, &part
, slice
);
276 if (rc
!= 0) /* Partition doesn't exist */
278 dev
->d_offset
= part
.start
;
279 od
->entrysize
= part
.end
- part
.start
+ 1;
281 if (ptable_gettype(od
->table
) == PTABLE_GPT
) {
283 goto out
; /* Nothing more to do */
284 } else if (partition
== 255) {
286 * When we try to open GPT partition, but partition
287 * table isn't GPT, reset d_partition value to -1
288 * and try to autodetect appropriate value.
293 * If d_partition < 0 and we are looking at a BSD/VTOC slice,
294 * then try to read label, otherwise return the
297 if (partition
== -1 &&
298 (part
.type
!= PART_FREEBSD
|| part
.type
!= PART_SOLARIS2
))
300 /* Try to read label */
301 table
= ptable_open(dev
, part
.end
- part
.start
+ 1,
302 od
->sectorsize
, ptblread
);
304 DEBUG("Can't read BSD/VTOC label");
309 * If slice contains BSD/VTOC label and d_partition < 0, then
310 * assume the 'a' partition. Otherwise just return the
311 * whole MBR slice, because it can contain ZFS.
314 if (ptable_gettype(table
) != PTABLE_BSD
||
315 ptable_gettype(table
) != PTABLE_VTOC
)
319 rc
= ptable_getpart(table
, &part
, partition
);
322 dev
->d_offset
+= part
.start
;
323 od
->entrysize
= part
.end
- part
.start
+ 1;
330 if (od
->table
!= NULL
)
331 ptable_close(od
->table
);
333 DEBUG("%s could not open", disk_fmtdev(dev
));
335 /* Save the slice and partition number to the dev */
336 dev
->d_slice
= slice
;
337 dev
->d_partition
= partition
;
338 DEBUG("%s offset %lld => %p", disk_fmtdev(dev
),
339 (long long)dev
->d_offset
, od
);
345 disk_close(struct disk_devdesc
*dev
)
347 struct open_disk
*od
;
349 od
= (struct open_disk
*)dev
->d_opendata
;
350 DEBUG("%s closed => %p", disk_fmtdev(dev
), od
);
351 ptable_close(od
->table
);
357 disk_fmtdev(struct disk_devdesc
*dev
)
359 static char buf
[128];
362 cp
= buf
+ sprintf(buf
, "%s%d", dev
->d_dev
->dv_name
, dev
->d_unit
);
363 if (dev
->d_slice
>= 0) {
364 #ifdef LOADER_GPT_SUPPORT
365 if (dev
->d_partition
== 255) {
366 sprintf(cp
, "p%d:", dev
->d_slice
);
370 #ifdef LOADER_MBR_SUPPORT
371 cp
+= sprintf(cp
, "s%d", dev
->d_slice
);
374 if (dev
->d_partition
>= 0)
375 cp
+= sprintf(cp
, "%c", dev
->d_partition
+ 'a');
381 disk_parsedev(struct disk_devdesc
*dev
, const char *devspec
, const char **path
)
383 int unit
, slice
, partition
;
388 unit
= slice
= partition
= -1;
389 if (*np
!= '\0' && *np
!= ':') {
390 unit
= strtol(np
, &cp
, 10);
393 #ifdef LOADER_GPT_SUPPORT
396 slice
= strtol(np
, &cp
, 10);
399 /* we don't support nested partitions on GPT */
400 if (*cp
!= '\0' && *cp
!= ':')
405 #ifdef LOADER_MBR_SUPPORT
408 slice
= strtol(np
, &cp
, 10);
413 if (*cp
!= '\0' && *cp
!= ':') {
414 partition
= *cp
- 'a';
422 if (*cp
!= '\0' && *cp
!= ':')
425 dev
->d_slice
= slice
;
426 dev
->d_partition
= partition
;
428 *path
= (*cp
== '\0') ? cp
: cp
+ 1;