1223695Sdfr/*-
2223695Sdfr * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3239058Sae * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
4223695Sdfr * All rights reserved.
5223695Sdfr *
6223695Sdfr * Redistribution and use in source and binary forms, with or without
7223695Sdfr * modification, are permitted provided that the following conditions
8223695Sdfr * are met:
9223695Sdfr * 1. Redistributions of source code must retain the above copyright
10223695Sdfr *    notice, this list of conditions and the following disclaimer.
11223695Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12223695Sdfr *    notice, this list of conditions and the following disclaimer in the
13223695Sdfr *    documentation and/or other materials provided with the distribution.
14223695Sdfr *
15223695Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16223695Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17223695Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18223695Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19223695Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20223695Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21223695Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22223695Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23223695Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24223695Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25223695Sdfr * SUCH DAMAGE.
26223695Sdfr */
27223695Sdfr
28223695Sdfr#include <sys/cdefs.h>
29223695Sdfr__FBSDID("$FreeBSD: stable/11/stand/common/disk.c 347182 2019-05-06 08:55:23Z tsoome $");
30223695Sdfr
31239058Sae#include <sys/disk.h>
32241053Sae#include <sys/queue.h>
33223695Sdfr#include <stand.h>
34223695Sdfr#include <stdarg.h>
35223695Sdfr#include <bootstrap.h>
36239058Sae#include <part.h>
37223695Sdfr
38223695Sdfr#include "disk.h"
39223695Sdfr
40223695Sdfr#ifdef DISK_DEBUG
41346483Skevans# define DPRINTF(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
42223695Sdfr#else
43346483Skevans# define DPRINTF(fmt, args...)
44223695Sdfr#endif
45223695Sdfr
46239058Saestruct open_disk {
47239058Sae	struct ptable		*table;
48329099Skevans	uint64_t		mediasize;
49329099Skevans	uint64_t		entrysize;
50239058Sae	u_int			sectorsize;
51239058Sae};
52223695Sdfr
53239058Saestruct print_args {
54239058Sae	struct disk_devdesc	*dev;
55239058Sae	const char		*prefix;
56239058Sae	int			verbose;
57223695Sdfr};
58223695Sdfr
59239058Sae/* Convert size to a human-readable number. */
60223695Sdfrstatic char *
61239058Saedisplay_size(uint64_t size, u_int sectorsize)
62223695Sdfr{
63223695Sdfr	static char buf[80];
64223695Sdfr	char unit;
65223695Sdfr
66239058Sae	size = size * sectorsize / 1024;
67223695Sdfr	unit = 'K';
68223695Sdfr	if (size >= 10485760000LL) {
69223695Sdfr		size /= 1073741824;
70223695Sdfr		unit = 'T';
71223695Sdfr	} else if (size >= 10240000) {
72223695Sdfr		size /= 1048576;
73223695Sdfr		unit = 'G';
74223695Sdfr	} else if (size >= 10000) {
75223695Sdfr		size /= 1024;
76223695Sdfr		unit = 'M';
77223695Sdfr	}
78346483Skevans	sprintf(buf, "%4ld%cB", (long)size, unit);
79223695Sdfr	return (buf);
80223695Sdfr}
81223695Sdfr
82296963Sallanjudeint
83329099Skevansptblread(void *d, void *buf, size_t blocks, uint64_t offset)
84223695Sdfr{
85239058Sae	struct disk_devdesc *dev;
86239058Sae	struct open_disk *od;
87223695Sdfr
88239058Sae	dev = (struct disk_devdesc *)d;
89332154Skevans	od = (struct open_disk *)dev->dd.d_opendata;
90329100Skevans
91329100Skevans	/*
92329140Skevans	 * The strategy function assumes the offset is in units of 512 byte
93329140Skevans	 * sectors. For larger sector sizes, we need to adjust the offset to
94329140Skevans	 * match the actual sector size.
95329140Skevans	 */
96329140Skevans	offset *= (od->sectorsize / 512);
97329140Skevans	/*
98329100Skevans	 * As the GPT backup partition is located at the end of the disk,
99329100Skevans	 * to avoid reading past disk end, flag bcache not to use RA.
100329100Skevans	 */
101332154Skevans	return (dev->dd.d_dev->dv_strategy(dev, F_READ | F_NORA, offset,
102239058Sae	    blocks * od->sectorsize, (char *)buf, NULL));
103223695Sdfr}
104223695Sdfr
105300117Simpstatic int
106239058Saeptable_print(void *arg, const char *pname, const struct ptable_entry *part)
107223695Sdfr{
108329099Skevans	struct disk_devdesc dev;
109239058Sae	struct print_args *pa, bsd;
110239058Sae	struct open_disk *od;
111239058Sae	struct ptable *table;
112239058Sae	char line[80];
113300117Simp	int res;
114346483Skevans	u_int sectsize;
115346483Skevans	uint64_t partsize;
116223695Sdfr
117239058Sae	pa = (struct print_args *)arg;
118332154Skevans	od = (struct open_disk *)pa->dev->dd.d_opendata;
119346483Skevans	sectsize = od->sectorsize;
120346483Skevans	partsize = part->end - part->start + 1;
121346483Skevans	sprintf(line, "  %s%s: %s\t%s\n", pa->prefix, pname,
122346483Skevans	    parttype2str(part->type),
123346483Skevans	    pa->verbose ? display_size(partsize, sectsize) : "");
124300117Simp	if (pager_output(line))
125300117Simp		return 1;
126300117Simp	res = 0;
127239058Sae	if (part->type == PART_FREEBSD) {
128239058Sae		/* Open slice with BSD label */
129332154Skevans		dev.dd.d_dev = pa->dev->dd.d_dev;
130332154Skevans		dev.dd.d_unit = pa->dev->dd.d_unit;
131329099Skevans		dev.d_slice = part->index;
132329099Skevans		dev.d_partition = -1;
133346483Skevans		if (disk_open(&dev, partsize, sectsize) == 0) {
134346483Skevans			/*
135346483Skevans			 * disk_open() for partition -1 on a bsd slice assumes
136346483Skevans			 * you want the first bsd partition.  Reset things so
137346483Skevans			 * that we're looking at the start of the raw slice.
138346483Skevans			 */
139346483Skevans			dev.d_partition = -1;
140346483Skevans			dev.d_offset = part->start;
141346483Skevans			table = ptable_open(&dev, partsize, sectsize, ptblread);
142329099Skevans			if (table != NULL) {
143329099Skevans				sprintf(line, "  %s%s", pa->prefix, pname);
144329099Skevans				bsd.dev = pa->dev;
145329099Skevans				bsd.prefix = line;
146329099Skevans				bsd.verbose = pa->verbose;
147329099Skevans				res = ptable_iterate(table, &bsd, ptable_print);
148329099Skevans				ptable_close(table);
149329099Skevans			}
150329099Skevans			disk_close(&dev);
151329099Skevans		}
152223695Sdfr	}
153300117Simp
154300117Simp	return (res);
155223695Sdfr}
156223695Sdfr
157300117Simpint
158239058Saedisk_print(struct disk_devdesc *dev, char *prefix, int verbose)
159223695Sdfr{
160239058Sae	struct open_disk *od;
161239058Sae	struct print_args pa;
162223695Sdfr
163239058Sae	/* Disk should be opened */
164332154Skevans	od = (struct open_disk *)dev->dd.d_opendata;
165239058Sae	pa.dev = dev;
166239058Sae	pa.prefix = prefix;
167239058Sae	pa.verbose = verbose;
168300117Simp	return (ptable_iterate(od->table, &pa, ptable_print));
169223695Sdfr}
170223695Sdfr
171239058Saeint
172329099Skevansdisk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
173291402Szbb{
174291402Szbb	struct open_disk *od;
175291402Szbb	int ret;
176291402Szbb
177332154Skevans	od = (struct open_disk *)dev->dd.d_opendata;
178332154Skevans	ret = dev->dd.d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset,
179291402Szbb	    blocks * od->sectorsize, buf, NULL);
180291402Szbb
181291402Szbb	return (ret);
182291402Szbb}
183291402Szbb
184291402Szbbint
185329099Skevansdisk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
186291402Szbb{
187291402Szbb	struct open_disk *od;
188291402Szbb	int ret;
189291402Szbb
190332154Skevans	od = (struct open_disk *)dev->dd.d_opendata;
191332154Skevans	ret = dev->dd.d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset,
192291402Szbb	    blocks * od->sectorsize, buf, NULL);
193291402Szbb
194291402Szbb	return (ret);
195291402Szbb}
196291402Szbb
197291402Szbbint
198329099Skevansdisk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
199291402Szbb{
200332154Skevans	struct open_disk *od = dev->dd.d_opendata;
201291402Szbb
202329099Skevans	if (od == NULL)
203329099Skevans		return (ENOTTY);
204291402Szbb
205329099Skevans	switch (cmd) {
206329099Skevans	case DIOCGSECTORSIZE:
207329099Skevans		*(u_int *)data = od->sectorsize;
208329099Skevans		break;
209329099Skevans	case DIOCGMEDIASIZE:
210329099Skevans		if (dev->d_offset == 0)
211329099Skevans			*(uint64_t *)data = od->mediasize;
212329099Skevans		else
213329099Skevans			*(uint64_t *)data = od->entrysize * od->sectorsize;
214329099Skevans		break;
215329099Skevans	default:
216329099Skevans		return (ENOTTY);
217329099Skevans	}
218329099Skevans
219329099Skevans	return (0);
220291402Szbb}
221291402Szbb
222291402Szbbint
223329099Skevansdisk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize)
224223695Sdfr{
225344295Skevans	struct disk_devdesc partdev;
226239058Sae	struct open_disk *od;
227239058Sae	struct ptable *table;
228239058Sae	struct ptable_entry part;
229241053Sae	int rc, slice, partition;
230223695Sdfr
231347182Stsoome	if (sectorsize == 0) {
232347182Stsoome		DPRINTF("unknown sector size");
233347182Stsoome		return (ENXIO);
234347182Stsoome	}
235241809Sae	rc = 0;
236329099Skevans	od = (struct open_disk *)malloc(sizeof(struct open_disk));
237329099Skevans	if (od == NULL) {
238346483Skevans		DPRINTF("no memory");
239329099Skevans		return (ENOMEM);
240241053Sae	}
241332154Skevans	dev->dd.d_opendata = od;
242329099Skevans	od->entrysize = 0;
243239058Sae	od->mediasize = mediasize;
244239058Sae	od->sectorsize = sectorsize;
245344295Skevans	/*
246344295Skevans	 * While we are reading disk metadata, make sure we do it relative
247344295Skevans	 * to the start of the disk
248344295Skevans	 */
249344295Skevans	memcpy(&partdev, dev, sizeof(partdev));
250344295Skevans	partdev.d_offset = 0;
251344295Skevans	partdev.d_slice = -1;
252344295Skevans	partdev.d_partition = -1;
253344295Skevans
254344295Skevans	dev->d_offset = 0;
255344295Skevans	table = NULL;
256344295Skevans	slice = dev->d_slice;
257344295Skevans	partition = dev->d_partition;
258344295Skevans
259346483Skevans	DPRINTF("%s unit %d, slice %d, partition %d => %p",
260344295Skevans	    disk_fmtdev(dev), dev->dd.d_unit, dev->d_slice, dev->d_partition, od);
261223695Sdfr
262239058Sae	/* Determine disk layout. */
263344295Skevans	od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize,
264239058Sae	    ptblread);
265239058Sae	if (od->table == NULL) {
266346483Skevans		DPRINTF("Can't read partition table");
267239058Sae		rc = ENXIO;
268223695Sdfr		goto out;
269223695Sdfr	}
270329099Skevans
271329099Skevans	if (ptable_getsize(od->table, &mediasize) != 0) {
272329099Skevans		rc = ENXIO;
273329099Skevans		goto out;
274329099Skevans	}
275346475Skevans	od->mediasize = mediasize;
276329099Skevans
277239230Sae	if (ptable_gettype(od->table) == PTABLE_BSD &&
278241053Sae	    partition >= 0) {
279239230Sae		/* It doesn't matter what value has d_slice */
280241053Sae		rc = ptable_getpart(od->table, &part, partition);
281329099Skevans		if (rc == 0) {
282239230Sae			dev->d_offset = part.start;
283329099Skevans			od->entrysize = part.end - part.start + 1;
284329099Skevans		}
285332956Sbenno	} else if (ptable_gettype(od->table) == PTABLE_ISO9660) {
286332956Sbenno		dev->d_offset = 0;
287332956Sbenno		od->entrysize = mediasize;
288241053Sae	} else if (slice >= 0) {
289239058Sae		/* Try to get information about partition */
290241053Sae		if (slice == 0)
291241023Sae			rc = ptable_getbestpart(od->table, &part);
292241023Sae		else
293241053Sae			rc = ptable_getpart(od->table, &part, slice);
294239058Sae		if (rc != 0) /* Partition doesn't exist */
295223695Sdfr			goto out;
296239058Sae		dev->d_offset = part.start;
297329099Skevans		od->entrysize = part.end - part.start + 1;
298241053Sae		slice = part.index;
299241053Sae		if (ptable_gettype(od->table) == PTABLE_GPT) {
300241053Sae			partition = 255;
301241053Sae			goto out; /* Nothing more to do */
302241876Sae		} else if (partition == 255) {
303241876Sae			/*
304241876Sae			 * When we try to open GPT partition, but partition
305241876Sae			 * table isn't GPT, reset d_partition value to -1
306241876Sae			 * and try to autodetect appropriate value.
307241876Sae			 */
308241876Sae			partition = -1;
309241023Sae		}
310239293Sae		/*
311239293Sae		 * If d_partition < 0 and we are looking at a BSD slice,
312239293Sae		 * then try to read BSD label, otherwise return the
313239293Sae		 * whole MBR slice.
314239293Sae		 */
315241053Sae		if (partition == -1 &&
316239293Sae		    part.type != PART_FREEBSD)
317239293Sae			goto out;
318239058Sae		/* Try to read BSD label */
319239058Sae		table = ptable_open(dev, part.end - part.start + 1,
320239058Sae		    od->sectorsize, ptblread);
321239058Sae		if (table == NULL) {
322346483Skevans			DPRINTF("Can't read BSD label");
323239058Sae			rc = ENXIO;
324223695Sdfr			goto out;
325223695Sdfr		}
326239293Sae		/*
327239293Sae		 * If slice contains BSD label and d_partition < 0, then
328239293Sae		 * assume the 'a' partition. Otherwise just return the
329239293Sae		 * whole MBR slice, because it can contain ZFS.
330239293Sae		 */
331241053Sae		if (partition < 0) {
332239293Sae			if (ptable_gettype(table) != PTABLE_BSD)
333239293Sae				goto out;
334241053Sae			partition = 0;
335239293Sae		}
336241053Sae		rc = ptable_getpart(table, &part, partition);
337239058Sae		if (rc != 0)
338239058Sae			goto out;
339239058Sae		dev->d_offset += part.start;
340329099Skevans		od->entrysize = part.end - part.start + 1;
341223695Sdfr	}
342223695Sdfrout:
343239058Sae	if (table != NULL)
344239058Sae		ptable_close(table);
345239210Sae
346239058Sae	if (rc != 0) {
347329099Skevans		if (od->table != NULL)
348329099Skevans			ptable_close(od->table);
349329099Skevans		free(od);
350346483Skevans		DPRINTF("%s could not open", disk_fmtdev(dev));
351239210Sae	} else {
352241053Sae		/* Save the slice and partition number to the dev */
353241053Sae		dev->d_slice = slice;
354241053Sae		dev->d_partition = partition;
355346483Skevans		DPRINTF("%s offset %lld => %p", disk_fmtdev(dev),
356272557Sae		    (long long)dev->d_offset, od);
357239058Sae	}
358223695Sdfr	return (rc);
359223695Sdfr}
360223695Sdfr
361239058Saeint
362239058Saedisk_close(struct disk_devdesc *dev)
363223695Sdfr{
364239058Sae	struct open_disk *od;
365223695Sdfr
366332154Skevans	od = (struct open_disk *)dev->dd.d_opendata;
367346483Skevans	DPRINTF("%s closed => %p", disk_fmtdev(dev), od);
368329099Skevans	ptable_close(od->table);
369329099Skevans	free(od);
370223695Sdfr	return (0);
371223695Sdfr}
372223695Sdfr
373239058Saechar*
374239058Saedisk_fmtdev(struct disk_devdesc *dev)
375223695Sdfr{
376239058Sae	static char buf[128];
377239058Sae	char *cp;
378223695Sdfr
379332154Skevans	cp = buf + sprintf(buf, "%s%d", dev->dd.d_dev->dv_name, dev->dd.d_unit);
380241053Sae	if (dev->d_slice >= 0) {
381223695Sdfr#ifdef LOADER_GPT_SUPPORT
382239058Sae		if (dev->d_partition == 255) {
383239058Sae			sprintf(cp, "p%d:", dev->d_slice);
384239058Sae			return (buf);
385239058Sae		} else
386223695Sdfr#endif
387223712Smarius#ifdef LOADER_MBR_SUPPORT
388239058Sae			cp += sprintf(cp, "s%d", dev->d_slice);
389223712Smarius#endif
390239058Sae	}
391239230Sae	if (dev->d_partition >= 0)
392239230Sae		cp += sprintf(cp, "%c", dev->d_partition + 'a');
393239058Sae	strcat(cp, ":");
394239058Sae	return (buf);
395223695Sdfr}
396223695Sdfr
397239058Saeint
398239058Saedisk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
399223695Sdfr{
400239058Sae	int unit, slice, partition;
401239058Sae	const char *np;
402239058Sae	char *cp;
403223695Sdfr
404239058Sae	np = devspec;
405239058Sae	unit = slice = partition = -1;
406239058Sae	if (*np != '\0' && *np != ':') {
407239058Sae		unit = strtol(np, &cp, 10);
408239058Sae		if (cp == np)
409239058Sae			return (EUNIT);
410223695Sdfr#ifdef LOADER_GPT_SUPPORT
411239058Sae		if (*cp == 'p') {
412239058Sae			np = cp + 1;
413239058Sae			slice = strtol(np, &cp, 10);
414239058Sae			if (np == cp)
415239058Sae				return (ESLICE);
416239058Sae			/* we don't support nested partitions on GPT */
417239058Sae			if (*cp != '\0' && *cp != ':')
418239058Sae				return (EINVAL);
419239058Sae			partition = 255;
420239058Sae		} else
421223695Sdfr#endif
422223712Smarius#ifdef LOADER_MBR_SUPPORT
423239058Sae		if (*cp == 's') {
424239058Sae			np = cp + 1;
425239058Sae			slice = strtol(np, &cp, 10);
426239058Sae			if (np == cp)
427239058Sae				return (ESLICE);
428239058Sae		}
429223712Smarius#endif
430239058Sae		if (*cp != '\0' && *cp != ':') {
431239058Sae			partition = *cp - 'a';
432239058Sae			if (partition < 0)
433239058Sae				return (EPART);
434239058Sae			cp++;
435239058Sae		}
436239058Sae	} else
437239058Sae		return (EINVAL);
438239058Sae
439239058Sae	if (*cp != '\0' && *cp != ':')
440239058Sae		return (EINVAL);
441332154Skevans	dev->dd.d_unit = unit;
442239058Sae	dev->d_slice = slice;
443239058Sae	dev->d_partition = partition;
444239058Sae	if (path != NULL)
445239058Sae		*path = (*cp == '\0') ? cp: cp + 1;
446239058Sae	return (0);
447223695Sdfr}
448