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 * As the GPT backup partition is located at the end of the disk,
91 * to avoid reading past disk end, flag bcache not to use RA.
93 return (dev
->d_dev
->dv_strategy(dev
, F_READ
| F_NORA
, offset
,
94 blocks
* od
->sectorsize
, (char *)buf
, NULL
));
99 ptable_print(void *arg
, const char *pname
, const struct ptable_entry
*part
)
101 struct disk_devdesc dev
;
102 struct print_args
*pa
, bsd
;
103 struct open_disk
*od
;
104 struct ptable
*table
;
108 pa
= (struct print_args
*)arg
;
109 od
= (struct open_disk
*)pa
->dev
->d_opendata
;
110 sprintf(line
, " %s%s: %s", pa
->prefix
, pname
,
111 parttype2str(part
->type
));
113 sprintf(line
, "%-*s%s", PWIDTH
, line
,
114 display_size(part
->end
- part
->start
+ 1,
117 ret
= pager_output(line
);
120 if (part
->type
== PART_FREEBSD
|| part
->type
== PART_SOLARIS2
) {
121 /* Open slice with BSD or VTOC label */
122 dev
.d_dev
= pa
->dev
->d_dev
;
123 dev
.d_unit
= pa
->dev
->d_unit
;
124 dev
.d_slice
= part
->index
;
125 dev
.d_partition
= -1;
126 if (disk_open(&dev
, part
->end
- part
->start
+ 1,
127 od
->sectorsize
) == 0) {
128 table
= ptable_open(&dev
, part
->end
- part
->start
+ 1,
129 od
->sectorsize
, ptblread
);
131 sprintf(line
, " %s%s", pa
->prefix
, pname
);
134 bsd
.verbose
= pa
->verbose
;
135 ret
= ptable_iterate(table
, &bsd
, ptable_print
);
146 disk_print(struct disk_devdesc
*dev
, char *prefix
, int verbose
)
148 struct open_disk
*od
;
149 struct print_args pa
;
151 /* Disk should be opened */
152 od
= (struct open_disk
*)dev
->d_opendata
;
155 pa
.verbose
= verbose
;
156 return (ptable_iterate(od
->table
, &pa
, ptable_print
));
160 disk_read(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, u_int blocks
)
162 struct open_disk
*od
;
165 od
= (struct open_disk
*)dev
->d_opendata
;
166 ret
= dev
->d_dev
->dv_strategy(dev
, F_READ
, dev
->d_offset
+ offset
,
167 blocks
* od
->sectorsize
, buf
, NULL
);
173 disk_write(struct disk_devdesc
*dev
, void *buf
, uint64_t offset
, u_int blocks
)
175 struct open_disk
*od
;
178 od
= (struct open_disk
*)dev
->d_opendata
;
179 ret
= dev
->d_dev
->dv_strategy(dev
, F_WRITE
, dev
->d_offset
+ offset
,
180 blocks
* od
->sectorsize
, buf
, NULL
);
186 disk_ioctl(struct disk_devdesc
*dev
, u_long cmd
, void *data
)
188 struct open_disk
*od
= dev
->d_opendata
;
194 case DIOCGSECTORSIZE
:
195 *(u_int
*)data
= od
->sectorsize
;
198 if (dev
->d_offset
== 0)
199 *(uint64_t *)data
= od
->mediasize
;
201 *(uint64_t *)data
= od
->entrysize
* od
->sectorsize
;
211 disk_open(struct disk_devdesc
*dev
, uint64_t mediasize
, u_int sectorsize
)
213 struct open_disk
*od
;
214 struct ptable
*table
;
215 struct ptable_entry part
;
216 int rc
, slice
, partition
;
220 * While we are reading disk metadata, make sure we do it relative
221 * to the start of the disk
225 slice
= dev
->d_slice
;
226 partition
= dev
->d_partition
;
227 od
= (struct open_disk
*)malloc(sizeof(struct open_disk
));
232 dev
->d_opendata
= od
;
234 od
->mediasize
= mediasize
;
235 od
->sectorsize
= sectorsize
;
236 DEBUG("%s unit %d, slice %d, partition %d => %p",
237 disk_fmtdev(dev
), dev
->d_unit
, dev
->d_slice
, dev
->d_partition
, od
);
239 /* Determine disk layout. */
240 od
->table
= ptable_open(dev
, mediasize
/ sectorsize
, sectorsize
,
242 if (od
->table
== NULL
) {
243 DEBUG("Can't read partition table");
248 if (ptable_getsize(od
->table
, &mediasize
) != 0) {
252 if (mediasize
> od
->mediasize
) {
253 od
->mediasize
= mediasize
;
256 if (ptable_gettype(od
->table
) == PTABLE_BSD
&&
258 /* It doesn't matter what value has d_slice */
259 rc
= ptable_getpart(od
->table
, &part
, partition
);
261 dev
->d_offset
= part
.start
;
262 od
->entrysize
= part
.end
- part
.start
+ 1;
264 } else if (slice
>= 0) {
265 /* Try to get information about partition */
267 rc
= ptable_getbestpart(od
->table
, &part
);
269 rc
= ptable_getpart(od
->table
, &part
, slice
);
270 if (rc
!= 0) /* Partition doesn't exist */
272 dev
->d_offset
= part
.start
;
273 od
->entrysize
= part
.end
- part
.start
+ 1;
275 if (ptable_gettype(od
->table
) == PTABLE_GPT
) {
277 goto out
; /* Nothing more to do */
278 } else if (partition
== 255) {
280 * When we try to open GPT partition, but partition
281 * table isn't GPT, reset d_partition value to -1
282 * and try to autodetect appropriate value.
287 * If d_partition < 0 and we are looking at a BSD/VTOC slice,
288 * then try to read label, otherwise return the
291 if (partition
== -1 &&
292 (part
.type
!= PART_FREEBSD
|| part
.type
!= PART_SOLARIS2
))
294 /* Try to read label */
295 table
= ptable_open(dev
, part
.end
- part
.start
+ 1,
296 od
->sectorsize
, ptblread
);
298 DEBUG("Can't read BSD/VTOC label");
303 * If slice contains BSD/VTOC label and d_partition < 0, then
304 * assume the 'a' partition. Otherwise just return the
305 * whole MBR slice, because it can contain ZFS.
308 if (ptable_gettype(table
) != PTABLE_BSD
||
309 ptable_gettype(table
) != PTABLE_VTOC
)
313 rc
= ptable_getpart(table
, &part
, partition
);
316 dev
->d_offset
+= part
.start
;
317 od
->entrysize
= part
.end
- part
.start
+ 1;
324 if (od
->table
!= NULL
)
325 ptable_close(od
->table
);
327 DEBUG("%s could not open", disk_fmtdev(dev
));
329 /* Save the slice and partition number to the dev */
330 dev
->d_slice
= slice
;
331 dev
->d_partition
= partition
;
332 DEBUG("%s offset %lld => %p", disk_fmtdev(dev
),
333 (long long)dev
->d_offset
, od
);
339 disk_close(struct disk_devdesc
*dev
)
341 struct open_disk
*od
;
343 od
= (struct open_disk
*)dev
->d_opendata
;
344 DEBUG("%s closed => %p", disk_fmtdev(dev
), od
);
345 ptable_close(od
->table
);
351 disk_fmtdev(struct disk_devdesc
*dev
)
353 static char buf
[128];
356 cp
= buf
+ sprintf(buf
, "%s%d", dev
->d_dev
->dv_name
, dev
->d_unit
);
357 if (dev
->d_slice
>= 0) {
358 #ifdef LOADER_GPT_SUPPORT
359 if (dev
->d_partition
== 255) {
360 sprintf(cp
, "p%d:", dev
->d_slice
);
364 #ifdef LOADER_MBR_SUPPORT
365 cp
+= sprintf(cp
, "s%d", dev
->d_slice
);
368 if (dev
->d_partition
>= 0)
369 cp
+= sprintf(cp
, "%c", dev
->d_partition
+ 'a');
375 disk_parsedev(struct disk_devdesc
*dev
, const char *devspec
, const char **path
)
377 int unit
, slice
, partition
;
382 unit
= slice
= partition
= -1;
383 if (*np
!= '\0' && *np
!= ':') {
384 unit
= strtol(np
, &cp
, 10);
387 #ifdef LOADER_GPT_SUPPORT
390 slice
= strtol(np
, &cp
, 10);
393 /* we don't support nested partitions on GPT */
394 if (*cp
!= '\0' && *cp
!= ':')
399 #ifdef LOADER_MBR_SUPPORT
402 slice
= strtol(np
, &cp
, 10);
407 if (*cp
!= '\0' && *cp
!= ':') {
408 partition
= *cp
- 'a';
416 if (*cp
!= '\0' && *cp
!= ':')
419 dev
->d_slice
= slice
;
420 dev
->d_partition
= partition
;
422 *path
= (*cp
== '\0') ? cp
: cp
+ 1;