10c946d80SToomas Soome /*
20c946d80SToomas Soome  * CDDL HEADER START
30c946d80SToomas Soome  *
40c946d80SToomas Soome  * The contents of this file are subject to the terms of the
50c946d80SToomas Soome  * Common Development and Distribution License (the "License").
60c946d80SToomas Soome  * You may not use this file except in compliance with the License.
70c946d80SToomas Soome  *
80c946d80SToomas Soome  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90c946d80SToomas Soome  * or http://www.opensolaris.org/os/licensing.
100c946d80SToomas Soome  * See the License for the specific language governing permissions
110c946d80SToomas Soome  * and limitations under the License.
120c946d80SToomas Soome  *
130c946d80SToomas Soome  * When distributing Covered Code, include this CDDL HEADER in each
140c946d80SToomas Soome  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150c946d80SToomas Soome  * If applicable, add the following below this CDDL HEADER, with the
160c946d80SToomas Soome  * fields enclosed by brackets "[]" replaced with your own identifying
170c946d80SToomas Soome  * information: Portions Copyright [yyyy] [name of copyright owner]
180c946d80SToomas Soome  *
190c946d80SToomas Soome  * CDDL HEADER END
200c946d80SToomas Soome  */
210c946d80SToomas Soome /*
220c946d80SToomas Soome  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
230c946d80SToomas Soome  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
24*d7802caeSToomas Soome  * Copyright 2019 Toomas Soome <tsoome@me.com>
250c946d80SToomas Soome  */
260c946d80SToomas Soome 
270c946d80SToomas Soome #include <stdio.h>
28*d7802caeSToomas Soome #include <stdbool.h>
290c946d80SToomas Soome #include <errno.h>
300c946d80SToomas Soome #include <unistd.h>
310c946d80SToomas Soome #include <fcntl.h>
320c946d80SToomas Soome #include <assert.h>
330c946d80SToomas Soome #include <locale.h>
340c946d80SToomas Soome #include <strings.h>
350c946d80SToomas Soome #include <libfdisk.h>
36bdecfb1eSToomas Soome #include <err.h>
37*d7802caeSToomas Soome #include <time.h>
38*d7802caeSToomas Soome #include <spawn.h>
390c946d80SToomas Soome 
400c946d80SToomas Soome #include <sys/dktp/fdisk.h>
410c946d80SToomas Soome #include <sys/dkio.h>
420c946d80SToomas Soome #include <sys/vtoc.h>
430c946d80SToomas Soome #include <sys/multiboot.h>
440c946d80SToomas Soome #include <sys/types.h>
450c946d80SToomas Soome #include <sys/stat.h>
460c946d80SToomas Soome #include <sys/sysmacros.h>
470c946d80SToomas Soome #include <sys/efi_partition.h>
48*d7802caeSToomas Soome #include <sys/queue.h>
49*d7802caeSToomas Soome #include <sys/mount.h>
50*d7802caeSToomas Soome #include <sys/mntent.h>
51*d7802caeSToomas Soome #include <sys/mnttab.h>
52*d7802caeSToomas Soome #include <sys/wait.h>
530c946d80SToomas Soome #include <libfstyp.h>
54bdecfb1eSToomas Soome #include <libgen.h>
550c946d80SToomas Soome #include <uuid/uuid.h>
560c946d80SToomas Soome 
570c946d80SToomas Soome #include "installboot.h"
58bdecfb1eSToomas Soome #include "bblk_einfo.h"
59bdecfb1eSToomas Soome #include "boot_utils.h"
60bdecfb1eSToomas Soome #include "mboot_extra.h"
610c946d80SToomas Soome #include "getresponse.h"
620c946d80SToomas Soome 
630c946d80SToomas Soome #ifndef	TEXT_DOMAIN
640c946d80SToomas Soome #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
650c946d80SToomas Soome #endif
660c946d80SToomas Soome 
670c946d80SToomas Soome /*
680c946d80SToomas Soome  * BIOS bootblock installation:
690c946d80SToomas Soome  *
700c946d80SToomas Soome  * 1. MBR is first sector of the disk. If the file system on target is
710c946d80SToomas Soome  *    ufs or zfs, the same MBR code is installed on first sector of the
720c946d80SToomas Soome  *    partition as well; this will allow to have real MBR sector to be
730c946d80SToomas Soome  *    replaced by some other boot loader and have illumos chainloaded.
740c946d80SToomas Soome  *
750c946d80SToomas Soome  * installboot will record the start LBA and size of stage2 code in MBR code.
760c946d80SToomas Soome  * On boot, the MBR code will read the stage2 code and executes it.
770c946d80SToomas Soome  *
780c946d80SToomas Soome  * 2. Stage2 location depends on file system type;
790c946d80SToomas Soome  *    In case of zfs, installboot will store stage2 to zfs bootblk area,
800c946d80SToomas Soome  *    which is 512k bytes from partition start and size is 3.5MB.
810c946d80SToomas Soome  *
820c946d80SToomas Soome  *    In case of ufs, the stage2 location is 50 512B sectors from
830c946d80SToomas Soome  *    Solaris2 MBR partition start, within boot slice, boot slice size is
840c946d80SToomas Soome  *    one cylinder.
850c946d80SToomas Soome  *
860c946d80SToomas Soome  *    In case of pcfs, the stage2 location is 50 512B sectors from beginning
870c946d80SToomas Soome  *    of the disk, filling the space between MBR and first partition.
880c946d80SToomas Soome  *    This location assumes no other bootloader and the space is one cylinder,
890c946d80SToomas Soome  *    as first partition is starting from cylinder 1.
900c946d80SToomas Soome  *
910c946d80SToomas Soome  *    In case of GPT partitioning and if file system is not zfs, the boot
920c946d80SToomas Soome  *    support is only possible with dedicated boot partition. For GPT,
930c946d80SToomas Soome  *    the current implementation is using BOOT partition, which must exist.
940c946d80SToomas Soome  *    BOOT partition does only contain raw boot blocks, without any file system.
950c946d80SToomas Soome  *
960c946d80SToomas Soome  * Loader stage2 is created with embedded version, by using fake multiboot (MB)
970c946d80SToomas Soome  * header within first 32k and EINFO block is at the end of the actual
980c946d80SToomas Soome  * boot block. MB header load_addr is set to 0 and load_end_addr is set to
990c946d80SToomas Soome  * actual block end, so the EINFO size is (file size - load_end_addr).
1000c946d80SToomas Soome  * installboot does also store the illumos boot partition LBA to MB space,
1010c946d80SToomas Soome  * starting from bss_end_addr structure member location; stage2 will
1020c946d80SToomas Soome  * detect the partition and file system based on this value.
1030c946d80SToomas Soome  *
1040c946d80SToomas Soome  * Stored location values in MBR/stage2 also mean the bootblocks must be
1050c946d80SToomas Soome  * reinstalled in case the partition content is relocated.
1060c946d80SToomas Soome  */
1070c946d80SToomas Soome 
108*d7802caeSToomas Soome static bool	write_mbr = false;
109*d7802caeSToomas Soome static bool	force_mbr = false;
110*d7802caeSToomas Soome static bool	force_update = false;
111*d7802caeSToomas Soome static bool	do_getinfo = false;
112*d7802caeSToomas Soome static bool	do_version = false;
113*d7802caeSToomas Soome static bool	do_mirror_bblk = false;
114*d7802caeSToomas Soome static bool	strip = false;
115*d7802caeSToomas Soome static bool	verbose_dump = false;
116*d7802caeSToomas Soome static size_t	sector_size = SECTOR_SIZE;
1170c946d80SToomas Soome 
1180c946d80SToomas Soome /* Versioning string, if present. */
1190c946d80SToomas Soome static char		*update_str;
1200c946d80SToomas Soome 
121*d7802caeSToomas Soome /* Default location of boot programs. */
122*d7802caeSToomas Soome static char		*boot_dir = "/boot";
123*d7802caeSToomas Soome 
124*d7802caeSToomas Soome /* Our boot programs */
125*d7802caeSToomas Soome #define	STAGE1		"pmbr"
126*d7802caeSToomas Soome #define	STAGE2		"gptzfsboot"
127*d7802caeSToomas Soome #define	BOOTIA32	"bootia32.efi"
128*d7802caeSToomas Soome #define	BOOTX64		"bootx64.efi"
129*d7802caeSToomas Soome #define	LOADER32	"loader32.efi"
130*d7802caeSToomas Soome #define	LOADER64	"loader64.efi"
131*d7802caeSToomas Soome 
132*d7802caeSToomas Soome static char *stage1;
133*d7802caeSToomas Soome static char *stage2;
134*d7802caeSToomas Soome static char *efi32;
135*d7802caeSToomas Soome static char *efi64;
136*d7802caeSToomas Soome 
137*d7802caeSToomas Soome #define	GRUB_VERSION_OFF (0x3e)
138*d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MAJOR 3
139*d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MINOR 2
140*d7802caeSToomas Soome #define	GRUB_VERSION (2 << 8 | 3) /* 3.2 */
141*d7802caeSToomas Soome 
142*d7802caeSToomas Soome #define	LOADER_VERSION (1)
143*d7802caeSToomas Soome #define	LOADER_JOYENT_VERSION (2)
144*d7802caeSToomas Soome 
145*d7802caeSToomas Soome typedef enum {
146*d7802caeSToomas Soome 	MBR_TYPE_UNKNOWN,
147*d7802caeSToomas Soome 	MBR_TYPE_GRUB1,
148*d7802caeSToomas Soome 	MBR_TYPE_LOADER,
149*d7802caeSToomas Soome 	MBR_TYPE_LOADER_JOYENT,
150*d7802caeSToomas Soome } mbr_type_t;
151*d7802caeSToomas Soome 
1520c946d80SToomas Soome /*
1530c946d80SToomas Soome  * Temporary buffer to store the first 32K of data looking for a multiboot
1540c946d80SToomas Soome  * signature.
1550c946d80SToomas Soome  */
1560c946d80SToomas Soome char			mboot_scan[MBOOT_SCAN_SIZE];
1570c946d80SToomas Soome 
1580c946d80SToomas Soome /* Function prototypes. */
1590c946d80SToomas Soome static void check_options(char *);
160*d7802caeSToomas Soome static int open_device(const char *);
161*d7802caeSToomas Soome static char *make_blkdev(const char *);
1620c946d80SToomas Soome 
163*d7802caeSToomas Soome static int read_bootblock_from_file(const char *, ib_bootblock_t *);
1640c946d80SToomas Soome static void add_bootblock_einfo(ib_bootblock_t *, char *);
165*d7802caeSToomas Soome static void prepare_bootblock(ib_data_t *, struct partlist *, char *);
166*d7802caeSToomas Soome static int handle_install(char *, int, char **);
167*d7802caeSToomas Soome static int handle_getinfo(char *, int, char **);
168*d7802caeSToomas Soome static int handle_mirror(char *, int, char **);
169bdecfb1eSToomas Soome static void usage(char *, int) __NORETURN;
1700c946d80SToomas Soome 
171*d7802caeSToomas Soome static char *
172*d7802caeSToomas Soome stagefs_mount(char *blkdev, struct partlist *plist)
1730c946d80SToomas Soome {
174*d7802caeSToomas Soome 	char *path;
175*d7802caeSToomas Soome 	char optbuf[MAX_MNTOPT_STR] = { '\0', };
176*d7802caeSToomas Soome 	char *template = strdup("/tmp/ibootXXXXXX");
177*d7802caeSToomas Soome 	int ret;
1780c946d80SToomas Soome 
179*d7802caeSToomas Soome 	if (template == NULL)
180*d7802caeSToomas Soome 		return (NULL);
1810c946d80SToomas Soome 
182*d7802caeSToomas Soome 	if ((path = mkdtemp(template)) == NULL) {
183*d7802caeSToomas Soome 		free(template);
184*d7802caeSToomas Soome 		return (NULL);
1850c946d80SToomas Soome 	}
186*d7802caeSToomas Soome 
187*d7802caeSToomas Soome 	(void) snprintf(optbuf, MAX_MNTOPT_STR, "timezone=%d",
188*d7802caeSToomas Soome 	    timezone);
189*d7802caeSToomas Soome 	ret = mount(blkdev, path, MS_OPTIONSTR,
190*d7802caeSToomas Soome 	    MNTTYPE_PCFS, NULL, 0, optbuf, MAX_MNTOPT_STR);
191*d7802caeSToomas Soome 	if (ret != 0) {
192*d7802caeSToomas Soome 		(void) rmdir(path);
193*d7802caeSToomas Soome 		free(path);
194*d7802caeSToomas Soome 		path = NULL;
195*d7802caeSToomas Soome 	}
196*d7802caeSToomas Soome 	plist->pl_device->stage.mntpnt = path;
197*d7802caeSToomas Soome 	return (path);
1980c946d80SToomas Soome }
1990c946d80SToomas Soome 
200*d7802caeSToomas Soome static void
201*d7802caeSToomas Soome install_stage1_cb(void *data, struct partlist *plist)
2020c946d80SToomas Soome {
203*d7802caeSToomas Soome 	int rv, fd;
204*d7802caeSToomas Soome 	ib_device_t *device = plist->pl_device;
2050c946d80SToomas Soome 
206*d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR && !write_mbr)
207*d7802caeSToomas Soome 		return;
2080c946d80SToomas Soome 
209*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
210*d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
211*d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
2120c946d80SToomas Soome 		perror("open");
213*d7802caeSToomas Soome 		return;
2140c946d80SToomas Soome 	}
2150c946d80SToomas Soome 
216*d7802caeSToomas Soome 	rv = write_out(fd, plist->pl_stage, sector_size, 0);
217*d7802caeSToomas Soome 	if (rv != BC_SUCCESS) {
218*d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot write "
219*d7802caeSToomas Soome 		    "partition boot sector\n"));
220*d7802caeSToomas Soome 		perror("write");
221*d7802caeSToomas Soome 	} else {
222*d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("stage1 written to "
223*d7802caeSToomas Soome 		    "%s %d sector 0 (abs %d)\n"),
224*d7802caeSToomas Soome 		    device->devtype == IB_DEV_MBR? "partition" : "slice",
225*d7802caeSToomas Soome 		    device->stage.id, device->stage.start);
2260c946d80SToomas Soome 	}
227*d7802caeSToomas Soome }
2280c946d80SToomas Soome 
229*d7802caeSToomas Soome static void
230*d7802caeSToomas Soome install_stage2_cb(void *data, struct partlist *plist)
231*d7802caeSToomas Soome {
232*d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
233*d7802caeSToomas Soome 	int fd, ret;
234*d7802caeSToomas Soome 	off_t offset;
235*d7802caeSToomas Soome 	uint64_t abs;
2360c946d80SToomas Soome 
237*d7802caeSToomas Soome 	/*
238*d7802caeSToomas Soome 	 * ZFS bootblock area is 3.5MB, make sure we can fit.
239*d7802caeSToomas Soome 	 * buf_size is size of bootblk+EINFO.
240*d7802caeSToomas Soome 	 */
241*d7802caeSToomas Soome 	if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
242*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("bootblock is too large\n"));
243*d7802caeSToomas Soome 		return;
244*d7802caeSToomas Soome 	}
2450c946d80SToomas Soome 
246*d7802caeSToomas Soome 	abs = plist->pl_device->stage.start + plist->pl_device->stage.offset;
247*d7802caeSToomas Soome 
248*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
249*d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
250*d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
251*d7802caeSToomas Soome 		perror("open");
252*d7802caeSToomas Soome 		return;
253*d7802caeSToomas Soome 	}
254*d7802caeSToomas Soome 	offset = plist->pl_device->stage.offset * SECTOR_SIZE;
255*d7802caeSToomas Soome 	ret = write_out(fd, bblock->buf, bblock->buf_size, offset);
256*d7802caeSToomas Soome 	(void) close(fd);
257*d7802caeSToomas Soome 	if (ret != BC_SUCCESS) {
258*d7802caeSToomas Soome 		BOOT_DEBUG("Error writing the ZFS bootblock "
259*d7802caeSToomas Soome 		    "to %s at offset %d\n", plist->pl_devname, offset);
260*d7802caeSToomas Soome 		return;
261*d7802caeSToomas Soome 	}
262*d7802caeSToomas Soome 	(void) fprintf(stdout, gettext("bootblock written for %s,"
263*d7802caeSToomas Soome 	    " %d sectors starting at %d (abs %lld)\n"), plist->pl_devname,
264*d7802caeSToomas Soome 	    (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
265*d7802caeSToomas Soome }
266*d7802caeSToomas Soome 
267*d7802caeSToomas Soome static bool
268*d7802caeSToomas Soome mkfs_pcfs(const char *dev)
269*d7802caeSToomas Soome {
270*d7802caeSToomas Soome 	pid_t pid, w;
271*d7802caeSToomas Soome 	posix_spawnattr_t attr;
272*d7802caeSToomas Soome 	posix_spawn_file_actions_t file_actions;
273*d7802caeSToomas Soome 	int status;
274*d7802caeSToomas Soome 	char *cmd[7];
275*d7802caeSToomas Soome 
276*d7802caeSToomas Soome 	if (posix_spawnattr_init(&attr))
277*d7802caeSToomas Soome 		return (false);
278*d7802caeSToomas Soome 	if (posix_spawn_file_actions_init(&file_actions)) {
279*d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
280*d7802caeSToomas Soome 		return (false);
281*d7802caeSToomas Soome 	}
282*d7802caeSToomas Soome 
283*d7802caeSToomas Soome 	if (posix_spawnattr_setflags(&attr,
284*d7802caeSToomas Soome 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP)) {
285*d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
286*d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
287*d7802caeSToomas Soome 		return (false);
288*d7802caeSToomas Soome 	}
289*d7802caeSToomas Soome 	if (posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null",
290*d7802caeSToomas Soome 	    O_RDONLY, 0)) {
291*d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
292*d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
293*d7802caeSToomas Soome 		return (false);
294*d7802caeSToomas Soome 	}
295*d7802caeSToomas Soome 
296*d7802caeSToomas Soome 	cmd[0] = "/usr/sbin/mkfs";
297*d7802caeSToomas Soome 	cmd[1] = "-F";
298*d7802caeSToomas Soome 	cmd[2] = "pcfs";
299*d7802caeSToomas Soome 	cmd[3] = "-o";
300*d7802caeSToomas Soome 	cmd[4] = "fat=32";
301*d7802caeSToomas Soome 	cmd[5] = (char *)dev;
302*d7802caeSToomas Soome 	cmd[6] = NULL;
303*d7802caeSToomas Soome 
304*d7802caeSToomas Soome 	if (posix_spawn(&pid, cmd[0], &file_actions, &attr, cmd, NULL))
305*d7802caeSToomas Soome 		return (false);
306*d7802caeSToomas Soome 	(void) posix_spawnattr_destroy(&attr);
307*d7802caeSToomas Soome 	(void) posix_spawn_file_actions_destroy(&file_actions);
308*d7802caeSToomas Soome 
309*d7802caeSToomas Soome 	do {
310*d7802caeSToomas Soome 		w = waitpid(pid, &status, 0);
311*d7802caeSToomas Soome 	} while (w == -1 && errno == EINTR);
312*d7802caeSToomas Soome 	if (w == -1)
313*d7802caeSToomas Soome 		status = -1;
314*d7802caeSToomas Soome 
315*d7802caeSToomas Soome 	return (status != -1);
316*d7802caeSToomas Soome }
317*d7802caeSToomas Soome 
318*d7802caeSToomas Soome static void
319*d7802caeSToomas Soome install_esp_cb(void *data, struct partlist *plist)
320*d7802caeSToomas Soome {
321*d7802caeSToomas Soome 	fstyp_handle_t fhdl;
322*d7802caeSToomas Soome 	const char *fident;
323*d7802caeSToomas Soome 	bool pcfs;
324*d7802caeSToomas Soome 	char *blkdev, *path, *file;
325*d7802caeSToomas Soome 	FILE *fp;
326*d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
327*d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
328*d7802caeSToomas Soome 	int fd, ret;
329*d7802caeSToomas Soome 
330*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
331*d7802caeSToomas Soome 		return;
332*d7802caeSToomas Soome 
333*d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
334*d7802caeSToomas Soome 		(void) close(fd);
335*d7802caeSToomas Soome 		return;
336*d7802caeSToomas Soome 	}
337*d7802caeSToomas Soome 
338*d7802caeSToomas Soome 	pcfs = false;
339*d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) == 0) {
340*d7802caeSToomas Soome 		if (strcmp(fident, MNTTYPE_PCFS) == 0)
341*d7802caeSToomas Soome 			pcfs = true;
342*d7802caeSToomas Soome 	}
343*d7802caeSToomas Soome 	fstyp_fini(fhdl);
344*d7802caeSToomas Soome 	(void) close(fd);
345*d7802caeSToomas Soome 
346*d7802caeSToomas Soome 	if (!pcfs) {
347*d7802caeSToomas Soome 		(void) printf(gettext("Creating pcfs on ESP %s\n"),
348*d7802caeSToomas Soome 		    plist->pl_devname);
349*d7802caeSToomas Soome 
350*d7802caeSToomas Soome 		if (!mkfs_pcfs(plist->pl_devname)) {
351*d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("mkfs -F pcfs failed "
352*d7802caeSToomas Soome 			    "on %s\n"), plist->pl_devname);
353*d7802caeSToomas Soome 			return;
354*d7802caeSToomas Soome 		}
355*d7802caeSToomas Soome 	}
356*d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
357*d7802caeSToomas Soome 	if (blkdev == NULL)
358*d7802caeSToomas Soome 		return;
359*d7802caeSToomas Soome 
360*d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
361*d7802caeSToomas Soome 	if (fp == NULL) {
362*d7802caeSToomas Soome 		perror("fopen");
363*d7802caeSToomas Soome 		free(blkdev);
364*d7802caeSToomas Soome 		return;
365*d7802caeSToomas Soome 	}
366*d7802caeSToomas Soome 
367*d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
368*d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
369*d7802caeSToomas Soome 	(void) fclose(fp);
370*d7802caeSToomas Soome 	if (ret == 0)
371*d7802caeSToomas Soome 		path = mp.mnt_mountp;
372*d7802caeSToomas Soome 	else
373*d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
374*d7802caeSToomas Soome 
375*d7802caeSToomas Soome 	free(blkdev);
376*d7802caeSToomas Soome 	if (path == NULL)
377*d7802caeSToomas Soome 		return;
378*d7802caeSToomas Soome 
379*d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, "/EFI") < 0) {
3800c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
381*d7802caeSToomas Soome 		return;
3820c946d80SToomas Soome 	}
3830c946d80SToomas Soome 
384*d7802caeSToomas Soome 	ret = mkdir(file, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
385*d7802caeSToomas Soome 	if (ret == 0 || errno == EEXIST) {
386*d7802caeSToomas Soome 		free(file);
387*d7802caeSToomas Soome 		if (asprintf(&file, "%s%s", path, "/EFI/Boot") < 0) {
388*d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
389*d7802caeSToomas Soome 			return;
390*d7802caeSToomas Soome 		}
391*d7802caeSToomas Soome 		ret = mkdir(file,
392*d7802caeSToomas Soome 		    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
393*d7802caeSToomas Soome 		if (errno == EEXIST)
394*d7802caeSToomas Soome 			ret = 0;
395*d7802caeSToomas Soome 	}
396*d7802caeSToomas Soome 	free(file);
397*d7802caeSToomas Soome 	if (ret < 0) {
398*d7802caeSToomas Soome 		perror("mkdir");
399*d7802caeSToomas Soome 		return;
4000c946d80SToomas Soome 	}
4010c946d80SToomas Soome 
402*d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
403*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
404*d7802caeSToomas Soome 		return;
405*d7802caeSToomas Soome 	}
406*d7802caeSToomas Soome 
407*d7802caeSToomas Soome 	/* Write stage file. Should create temp file and rename. */
408*d7802caeSToomas Soome 	(void) chmod(file, S_IRUSR | S_IWUSR);
409*d7802caeSToomas Soome 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
410*d7802caeSToomas Soome 	if (fd != -1) {
411*d7802caeSToomas Soome 		ret = write_out(fd, bblock->buf, bblock->buf_size, 0);
412*d7802caeSToomas Soome 		if (ret == BC_SUCCESS) {
413*d7802caeSToomas Soome 			(void) fprintf(stdout,
414*d7802caeSToomas Soome 			    gettext("bootblock written to %s\n"), file);
415*d7802caeSToomas Soome 		} else {
416*d7802caeSToomas Soome 			(void) fprintf(stdout,
417*d7802caeSToomas Soome 			    gettext("error while writing %s\n"), file);
418*d7802caeSToomas Soome 		}
419*d7802caeSToomas Soome 		(void) fchmod(fd, S_IRUSR | S_IRGRP | S_IROTH);
420*d7802caeSToomas Soome 		(void) close(fd);
421*d7802caeSToomas Soome 	}
422*d7802caeSToomas Soome 	free(file);
423*d7802caeSToomas Soome }
424*d7802caeSToomas Soome 
425*d7802caeSToomas Soome /*
426*d7802caeSToomas Soome  * MBR setup only depends on write_mbr toggle.
427*d7802caeSToomas Soome  */
428*d7802caeSToomas Soome static bool
429*d7802caeSToomas Soome compare_mbr_cb(struct partlist *plist)
430*d7802caeSToomas Soome {
431*d7802caeSToomas Soome 	/* get confirmation for -m */
432*d7802caeSToomas Soome 	if (write_mbr && !force_mbr) {
433*d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("Updating master boot sector "
434*d7802caeSToomas Soome 		    "destroys existing boot managers (if any).\n"
435*d7802caeSToomas Soome 		    "continue (y/n)? "));
436*d7802caeSToomas Soome 		if (!yes()) {
437*d7802caeSToomas Soome 			write_mbr = false;
438*d7802caeSToomas Soome 			(void) fprintf(stdout, gettext("master boot sector "
439*d7802caeSToomas Soome 			    "not updated\n"));
440*d7802caeSToomas Soome 		}
441*d7802caeSToomas Soome 	}
442*d7802caeSToomas Soome 	if (write_mbr)
443*d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
444*d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
445*d7802caeSToomas Soome 	return (write_mbr);
446*d7802caeSToomas Soome }
447*d7802caeSToomas Soome 
448*d7802caeSToomas Soome /*
449*d7802caeSToomas Soome  * VBR setup is always done.
450*d7802caeSToomas Soome  */
451*d7802caeSToomas Soome static bool
452*d7802caeSToomas Soome compare_stage1_cb(struct partlist *plist)
453*d7802caeSToomas Soome {
454*d7802caeSToomas Soome 	(void) printf("%s will be written to %s\n", plist->pl_src_name,
455*d7802caeSToomas Soome 	    plist->pl_devname);
456*d7802caeSToomas Soome 	return (true);
457*d7802caeSToomas Soome }
458*d7802caeSToomas Soome 
459*d7802caeSToomas Soome /*
460*d7802caeSToomas Soome  * Return true if we can update, false if not.
461*d7802caeSToomas Soome  */
462*d7802caeSToomas Soome static bool
463*d7802caeSToomas Soome compare_einfo_cb(struct partlist *plist)
464*d7802caeSToomas Soome {
465*d7802caeSToomas Soome 	ib_bootblock_t *bblock, *bblock_file;
466*d7802caeSToomas Soome 	bblk_einfo_t *einfo, *einfo_file;
467*d7802caeSToomas Soome 	bblk_hs_t bblock_hs;
468*d7802caeSToomas Soome 	bool rv;
469*d7802caeSToomas Soome 
470*d7802caeSToomas Soome 	bblock = plist->pl_stage;
471*d7802caeSToomas Soome 	if (bblock == NULL || bblock->extra == NULL || bblock->extra_size == 0)
472*d7802caeSToomas Soome 		return (true);
473*d7802caeSToomas Soome 
474*d7802caeSToomas Soome 	einfo = find_einfo(bblock->extra, bblock->extra_size);
475*d7802caeSToomas Soome 	if (einfo == NULL) {
476*d7802caeSToomas Soome 		BOOT_DEBUG("No extended information available on disk\n");
477*d7802caeSToomas Soome 		return (true);
478*d7802caeSToomas Soome 	}
479*d7802caeSToomas Soome 
480*d7802caeSToomas Soome 	bblock_file = plist->pl_src_data;
481*d7802caeSToomas Soome 	einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
482*d7802caeSToomas Soome 	if (einfo_file == NULL) {
483*d7802caeSToomas Soome 		/*
484*d7802caeSToomas Soome 		 * loader bootblock is versioned. missing version means
485*d7802caeSToomas Soome 		 * probably incompatible block. installboot can not install
486*d7802caeSToomas Soome 		 * grub, for example.
487*d7802caeSToomas Soome 		 */
4880c946d80SToomas Soome 		(void) fprintf(stderr,
489*d7802caeSToomas Soome 		    gettext("ERROR: non versioned bootblock in file\n"));
490*d7802caeSToomas Soome 		return (false);
491*d7802caeSToomas Soome 	} else {
492*d7802caeSToomas Soome 		if (update_str == NULL) {
493*d7802caeSToomas Soome 			update_str = einfo_get_string(einfo_file);
494*d7802caeSToomas Soome 			do_version = true;
495*d7802caeSToomas Soome 		}
4960c946d80SToomas Soome 	}
4970c946d80SToomas Soome 
498*d7802caeSToomas Soome 	if (!do_version || update_str == NULL) {
499*d7802caeSToomas Soome 		(void) fprintf(stderr,
500*d7802caeSToomas Soome 		    gettext("WARNING: target device %s has a "
501*d7802caeSToomas Soome 		    "versioned bootblock that is going to be overwritten by a "
502*d7802caeSToomas Soome 		    "non versioned one\n"), plist->pl_devname);
503*d7802caeSToomas Soome 		return (true);
504*d7802caeSToomas Soome 	}
5050c946d80SToomas Soome 
506*d7802caeSToomas Soome 	if (force_update) {
507*d7802caeSToomas Soome 		BOOT_DEBUG("Forcing update of %s bootblock\n",
508*d7802caeSToomas Soome 		    plist->pl_devname);
509*d7802caeSToomas Soome 		return (true);
510*d7802caeSToomas Soome 	}
5110c946d80SToomas Soome 
512*d7802caeSToomas Soome 	BOOT_DEBUG("Ready to check installed version vs %s\n", update_str);
5130c946d80SToomas Soome 
514*d7802caeSToomas Soome 	bblock_hs.src_buf = (unsigned char *)bblock_file->file;
515*d7802caeSToomas Soome 	bblock_hs.src_size = bblock_file->file_size;
516*d7802caeSToomas Soome 
517*d7802caeSToomas Soome 	rv = einfo_should_update(einfo, &bblock_hs, update_str);
518*d7802caeSToomas Soome 	if (rv == false) {
519*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("\nBootblock version installed "
520*d7802caeSToomas Soome 		    "on %s is more recent or identical to\n%s\n"
521*d7802caeSToomas Soome 		    "Use -F to override or install without the -u option.\n"),
522*d7802caeSToomas Soome 		    plist->pl_devname, plist->pl_src_name);
523*d7802caeSToomas Soome 	} else {
524*d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
525*d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
526*d7802caeSToomas Soome 	}
527*d7802caeSToomas Soome 	return (rv);
528*d7802caeSToomas Soome }
529*d7802caeSToomas Soome 
530*d7802caeSToomas Soome static bool
531*d7802caeSToomas Soome read_stage1_cb(struct partlist *plist)
532*d7802caeSToomas Soome {
533*d7802caeSToomas Soome 	int fd;
534*d7802caeSToomas Soome 	bool rv = false;
535*d7802caeSToomas Soome 
536*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
537*d7802caeSToomas Soome 		return (rv);
538*d7802caeSToomas Soome 
539*d7802caeSToomas Soome 	if (plist->pl_stage == NULL)
540*d7802caeSToomas Soome 		plist->pl_stage = calloc(1, sector_size);
541*d7802caeSToomas Soome 
542*d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
543*d7802caeSToomas Soome 		perror("calloc");
544*d7802caeSToomas Soome 		goto done;
545*d7802caeSToomas Soome 	}
5460c946d80SToomas Soome 
547*d7802caeSToomas Soome 	if (pread(fd, plist->pl_stage, sector_size, 0) == -1) {
548*d7802caeSToomas Soome 		perror("pread");
549*d7802caeSToomas Soome 		goto done;
550*d7802caeSToomas Soome 	}
551*d7802caeSToomas Soome 	rv = true;
552*d7802caeSToomas Soome done:
5530c946d80SToomas Soome 	(void) close(fd);
554*d7802caeSToomas Soome 	return (rv);
555*d7802caeSToomas Soome }
5560c946d80SToomas Soome 
557*d7802caeSToomas Soome static bool
558*d7802caeSToomas Soome read_stage1_bbl_cb(struct partlist *plist)
559*d7802caeSToomas Soome {
560*d7802caeSToomas Soome 	int fd;
561*d7802caeSToomas Soome 	void *data;
562*d7802caeSToomas Soome 	bool rv = false;
563*d7802caeSToomas Soome 
564*d7802caeSToomas Soome 	data = malloc(SECTOR_SIZE);
565*d7802caeSToomas Soome 	if (data == NULL)
566*d7802caeSToomas Soome 		return (rv);
567*d7802caeSToomas Soome 
568*d7802caeSToomas Soome 	/* read the stage1 file from filesystem */
569*d7802caeSToomas Soome 	fd = open(plist->pl_src_name, O_RDONLY);
570*d7802caeSToomas Soome 	if (fd == -1 ||
571*d7802caeSToomas Soome 	    read(fd, data, SECTOR_SIZE) != SECTOR_SIZE) {
572*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
573*d7802caeSToomas Soome 		    plist->pl_src_name);
574*d7802caeSToomas Soome 		free(data);
575*d7802caeSToomas Soome 		if (fd != -1)
576*d7802caeSToomas Soome 			(void) close(fd);
577*d7802caeSToomas Soome 		return (rv);
578*d7802caeSToomas Soome 	}
579*d7802caeSToomas Soome 
580*d7802caeSToomas Soome 	plist->pl_src_data = data;
5810c946d80SToomas Soome 	(void) close(fd);
582*d7802caeSToomas Soome 	return (true);
5830c946d80SToomas Soome }
5840c946d80SToomas Soome 
585*d7802caeSToomas Soome static bool
586*d7802caeSToomas Soome read_stage2_cb(struct partlist *plist)
5870c946d80SToomas Soome {
588*d7802caeSToomas Soome 	ib_device_t		*device;
589*d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
590*d7802caeSToomas Soome 	int			fd;
5910c946d80SToomas Soome 	uint32_t		size, offset;
5920c946d80SToomas Soome 	uint32_t		buf_size;
5930c946d80SToomas Soome 	uint32_t		mboot_off;
5940c946d80SToomas Soome 	multiboot_header_t	*mboot;
5950c946d80SToomas Soome 
596*d7802caeSToomas Soome 	bblock = calloc(1, sizeof (ib_bootblock_t));
597*d7802caeSToomas Soome 	if (bblock == NULL)
598*d7802caeSToomas Soome 		return (false);
5990c946d80SToomas Soome 
600*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
601*d7802caeSToomas Soome 		free(bblock);
602*d7802caeSToomas Soome 		return (false);
6030c946d80SToomas Soome 	}
6040c946d80SToomas Soome 
605*d7802caeSToomas Soome 	device = plist->pl_device;
606*d7802caeSToomas Soome 	plist->pl_stage = bblock;
607*d7802caeSToomas Soome 	offset = device->stage.offset * SECTOR_SIZE;
608*d7802caeSToomas Soome 
609*d7802caeSToomas Soome 	if (read_in(fd, mboot_scan, sizeof (mboot_scan), offset)
6100c946d80SToomas Soome 	    != BC_SUCCESS) {
6110c946d80SToomas Soome 		BOOT_DEBUG("Error reading bootblock area\n");
6120c946d80SToomas Soome 		perror("read");
613*d7802caeSToomas Soome 		(void) close(fd);
614*d7802caeSToomas Soome 		return (false);
6150c946d80SToomas Soome 	}
6160c946d80SToomas Soome 
6170c946d80SToomas Soome 	/* No multiboot means no chance of knowing bootblock size */
6180c946d80SToomas Soome 	if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
6190c946d80SToomas Soome 	    != BC_SUCCESS) {
6200c946d80SToomas Soome 		BOOT_DEBUG("Unable to find multiboot header\n");
621*d7802caeSToomas Soome 		(void) close(fd);
622*d7802caeSToomas Soome 		return (false);
6230c946d80SToomas Soome 	}
6240c946d80SToomas Soome 	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
6250c946d80SToomas Soome 
6260c946d80SToomas Soome 	/*
6270c946d80SToomas Soome 	 * make sure mboot has sane values
6280c946d80SToomas Soome 	 */
6290c946d80SToomas Soome 	if (mboot->load_end_addr == 0 ||
630*d7802caeSToomas Soome 	    mboot->load_end_addr < mboot->load_addr) {
631*d7802caeSToomas Soome 		(void) close(fd);
632*d7802caeSToomas Soome 		return (false);
633*d7802caeSToomas Soome 	}
6340c946d80SToomas Soome 
6350c946d80SToomas Soome 	/*
6360c946d80SToomas Soome 	 * Currently, the amount of space reserved for extra information
6370c946d80SToomas Soome 	 * is "fixed". We may have to scan for the terminating extra payload
6380c946d80SToomas Soome 	 * in the future.
6390c946d80SToomas Soome 	 */
6400c946d80SToomas Soome 	size = mboot->load_end_addr - mboot->load_addr;
6410c946d80SToomas Soome 	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
6420c946d80SToomas Soome 	bblock->file_size = size;
6430c946d80SToomas Soome 
6440c946d80SToomas Soome 	bblock->buf = malloc(buf_size);
6450c946d80SToomas Soome 	if (bblock->buf == NULL) {
6460c946d80SToomas Soome 		BOOT_DEBUG("Unable to allocate enough memory to read"
6470c946d80SToomas Soome 		    " the extra bootblock from the disk\n");
6480c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
649*d7802caeSToomas Soome 		(void) close(fd);
650*d7802caeSToomas Soome 		return (false);
6510c946d80SToomas Soome 	}
6520c946d80SToomas Soome 	bblock->buf_size = buf_size;
6530c946d80SToomas Soome 
654*d7802caeSToomas Soome 	if (read_in(fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
6550c946d80SToomas Soome 		BOOT_DEBUG("Error reading the bootblock\n");
6560c946d80SToomas Soome 		(void) free(bblock->buf);
6570c946d80SToomas Soome 		bblock->buf = NULL;
658*d7802caeSToomas Soome 		(void) close(fd);
659*d7802caeSToomas Soome 		return (false);
6600c946d80SToomas Soome 	}
6610c946d80SToomas Soome 
6620c946d80SToomas Soome 	/* Update pointers. */
6630c946d80SToomas Soome 	bblock->file = bblock->buf;
6640c946d80SToomas Soome 	bblock->mboot_off = mboot_off;
6650c946d80SToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
6660c946d80SToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
6670c946d80SToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
6680c946d80SToomas Soome 
6690c946d80SToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
6700c946d80SToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
6710c946d80SToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
6720c946d80SToomas Soome 
673*d7802caeSToomas Soome 	return (true);
6740c946d80SToomas Soome }
6750c946d80SToomas Soome 
676*d7802caeSToomas Soome static bool
677*d7802caeSToomas Soome read_einfo_file_cb(struct partlist *plist)
6780c946d80SToomas Soome {
679*d7802caeSToomas Soome 	plist->pl_stage = calloc(1, sizeof (ib_bootblock_t));
680*d7802caeSToomas Soome 	if (plist->pl_stage == NULL)
681*d7802caeSToomas Soome 		return (false);
6820c946d80SToomas Soome 
683*d7802caeSToomas Soome 	return (read_bootblock_from_file(plist->pl_devname,
684*d7802caeSToomas Soome 	    plist->pl_stage) == BC_SUCCESS);
685*d7802caeSToomas Soome }
6860c946d80SToomas Soome 
687*d7802caeSToomas Soome static bool
688*d7802caeSToomas Soome read_stage2_file_cb(struct partlist *plist)
689*d7802caeSToomas Soome {
690*d7802caeSToomas Soome 	plist->pl_src_data = calloc(1, sizeof (ib_bootblock_t));
691*d7802caeSToomas Soome 	if (plist->pl_src_data == NULL)
692*d7802caeSToomas Soome 		return (false);
6930c946d80SToomas Soome 
694*d7802caeSToomas Soome 	return (read_bootblock_from_file(plist->pl_src_name,
695*d7802caeSToomas Soome 	    plist->pl_src_data) == BC_SUCCESS);
696*d7802caeSToomas Soome }
6970c946d80SToomas Soome 
698*d7802caeSToomas Soome /*
699*d7802caeSToomas Soome  * convert /dev/rdsk/... to /dev/dsk/...
700*d7802caeSToomas Soome  */
701*d7802caeSToomas Soome static char *
702*d7802caeSToomas Soome make_blkdev(const char *path)
703*d7802caeSToomas Soome {
704*d7802caeSToomas Soome 	char *tmp;
705*d7802caeSToomas Soome 	char *ptr = strdup(path);
7060c946d80SToomas Soome 
707*d7802caeSToomas Soome 	if (ptr == NULL)
708*d7802caeSToomas Soome 		return (ptr);
7090c946d80SToomas Soome 
710*d7802caeSToomas Soome 	tmp = strstr(ptr, "rdsk");
711*d7802caeSToomas Soome 	if (tmp == NULL) {
712*d7802caeSToomas Soome 		free(ptr);
713*d7802caeSToomas Soome 		return (NULL); /* Something is very wrong */
7140c946d80SToomas Soome 	}
715*d7802caeSToomas Soome 	/* This is safe because we do shorten the string */
716*d7802caeSToomas Soome 	(void) memmove(tmp, tmp + 1, strlen(tmp));
717*d7802caeSToomas Soome 	return (ptr);
718*d7802caeSToomas Soome }
7190c946d80SToomas Soome 
720*d7802caeSToomas Soome /*
721*d7802caeSToomas Soome  * Try to mount ESP and read boot program.
722*d7802caeSToomas Soome  */
723*d7802caeSToomas Soome static bool
724*d7802caeSToomas Soome read_einfo_esp_cb(struct partlist *plist)
725*d7802caeSToomas Soome {
726*d7802caeSToomas Soome 	fstyp_handle_t fhdl;
727*d7802caeSToomas Soome 	const char *fident;
728*d7802caeSToomas Soome 	char *blkdev, *path, *file;
729*d7802caeSToomas Soome 	bool rv = false;
730*d7802caeSToomas Soome 	FILE *fp;
731*d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
732*d7802caeSToomas Soome 	int fd, ret;
7330c946d80SToomas Soome 
734*d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
735*d7802caeSToomas Soome 		return (rv);
7360c946d80SToomas Soome 
737*d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
738*d7802caeSToomas Soome 		(void) close(fd);
739*d7802caeSToomas Soome 		return (rv);
740*d7802caeSToomas Soome 	}
7410c946d80SToomas Soome 
742*d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
743*d7802caeSToomas Soome 		fstyp_fini(fhdl);
744*d7802caeSToomas Soome 		(void) close(fd);
745*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
746*d7802caeSToomas Soome 		    "system type\n"));
747*d7802caeSToomas Soome 		return (rv);
748*d7802caeSToomas Soome 	}
7490c946d80SToomas Soome 
750*d7802caeSToomas Soome 	/* We only do expect pcfs. */
751*d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_PCFS) != 0) {
752*d7802caeSToomas Soome 		(void) fprintf(stderr,
753*d7802caeSToomas Soome 		    gettext("File system %s is not supported.\n"), fident);
754*d7802caeSToomas Soome 		fstyp_fini(fhdl);
755*d7802caeSToomas Soome 		(void) close(fd);
756*d7802caeSToomas Soome 		return (rv);
757*d7802caeSToomas Soome 	}
758*d7802caeSToomas Soome 	fstyp_fini(fhdl);
759*d7802caeSToomas Soome 	(void) close(fd);
760*d7802caeSToomas Soome 
761*d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
762*d7802caeSToomas Soome 	if (blkdev == NULL)
763*d7802caeSToomas Soome 		return (rv);
764*d7802caeSToomas Soome 
765*d7802caeSToomas Soome 	/* mount ESP if needed, read boot program(s) and unmount. */
766*d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
767*d7802caeSToomas Soome 	if (fp == NULL) {
768*d7802caeSToomas Soome 		perror("fopen");
769*d7802caeSToomas Soome 		free(blkdev);
770*d7802caeSToomas Soome 		return (rv);
7710c946d80SToomas Soome 	}
7720c946d80SToomas Soome 
773*d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
774*d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
775*d7802caeSToomas Soome 	(void) fclose(fp);
776*d7802caeSToomas Soome 	if (ret == 0)
777*d7802caeSToomas Soome 		path = mp.mnt_mountp;
778*d7802caeSToomas Soome 	else
779*d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
780*d7802caeSToomas Soome 
781*d7802caeSToomas Soome 	free(blkdev);
782*d7802caeSToomas Soome 	if (path == NULL)
783*d7802caeSToomas Soome 		return (rv);
784*d7802caeSToomas Soome 
785*d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
786*d7802caeSToomas Soome 		return (rv);
787*d7802caeSToomas Soome 	}
788*d7802caeSToomas Soome 
789*d7802caeSToomas Soome 	plist->pl_stage = calloc(1, sizeof (ib_bootblock_t));
790*d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
791*d7802caeSToomas Soome 		free(file);
792*d7802caeSToomas Soome 		return (rv);
793*d7802caeSToomas Soome 	}
794*d7802caeSToomas Soome 	if (read_bootblock_from_file(file, plist->pl_stage) != BC_SUCCESS) {
795*d7802caeSToomas Soome 		free(plist->pl_stage);
796*d7802caeSToomas Soome 		plist->pl_stage = NULL;
797*d7802caeSToomas Soome 	} else {
798*d7802caeSToomas Soome 		rv = true;
799*d7802caeSToomas Soome 	}
800*d7802caeSToomas Soome 
801*d7802caeSToomas Soome 	free(file);
802*d7802caeSToomas Soome 	return (rv);
8030c946d80SToomas Soome }
8040c946d80SToomas Soome 
805*d7802caeSToomas Soome static void
806*d7802caeSToomas Soome print_stage1_cb(struct partlist *plist)
8070c946d80SToomas Soome {
808*d7802caeSToomas Soome 	struct mboot *mbr;
809*d7802caeSToomas Soome 	struct ipart *part;
810*d7802caeSToomas Soome 	mbr_type_t type = MBR_TYPE_UNKNOWN;
811*d7802caeSToomas Soome 	bool pmbr = false;
812*d7802caeSToomas Soome 	char *label;
8130c946d80SToomas Soome 
814*d7802caeSToomas Soome 	mbr = plist->pl_stage;
8150c946d80SToomas Soome 
816*d7802caeSToomas Soome 	if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
817*d7802caeSToomas Soome 		type = MBR_TYPE_GRUB1;
818*d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
819*d7802caeSToomas Soome 		type = MBR_TYPE_LOADER;
820*d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
821*d7802caeSToomas Soome 		type = MBR_TYPE_LOADER_JOYENT;
822*d7802caeSToomas Soome 	}
8230c946d80SToomas Soome 
824*d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
825*d7802caeSToomas Soome 	for (int i = 0; i < FD_NUMPART; i++) {
826*d7802caeSToomas Soome 		if (part[i].systid == EFI_PMBR)
827*d7802caeSToomas Soome 			pmbr = true;
828*d7802caeSToomas Soome 	}
8290c946d80SToomas Soome 
830*d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR)
831*d7802caeSToomas Soome 		label = pmbr ? "PMBR" : "MBR";
832*d7802caeSToomas Soome 	else
833*d7802caeSToomas Soome 		label = "VBR";
8340c946d80SToomas Soome 
835*d7802caeSToomas Soome 	printf("%s block from %s:\n", label, plist->pl_devname);
8360c946d80SToomas Soome 
837*d7802caeSToomas Soome 	switch (type) {
838*d7802caeSToomas Soome 	case MBR_TYPE_UNKNOWN:
839*d7802caeSToomas Soome 		printf("Format: unknown\n");
840*d7802caeSToomas Soome 		break;
841*d7802caeSToomas Soome 	case MBR_TYPE_GRUB1:
842*d7802caeSToomas Soome 		printf("Format: grub1\n");
843*d7802caeSToomas Soome 		break;
844*d7802caeSToomas Soome 	case MBR_TYPE_LOADER:
845*d7802caeSToomas Soome 		printf("Format: loader (illumos)\n");
846*d7802caeSToomas Soome 		break;
847*d7802caeSToomas Soome 	case MBR_TYPE_LOADER_JOYENT:
848*d7802caeSToomas Soome 		printf("Format: loader (joyent)\n");
849*d7802caeSToomas Soome 		break;
8500c946d80SToomas Soome 	}
8510c946d80SToomas Soome 
852*d7802caeSToomas Soome 	printf("Signature: 0x%hx (%s)\n", mbr->signature,
853*d7802caeSToomas Soome 	    mbr->signature == MBB_MAGIC ? "valid" : "invalid");
8540c946d80SToomas Soome 
855*d7802caeSToomas Soome 	printf("UniqueMBRDiskSignature: %#lx\n",
856*d7802caeSToomas Soome 	    *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
857*d7802caeSToomas Soome 
858*d7802caeSToomas Soome 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
859*d7802caeSToomas Soome 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
860*d7802caeSToomas Soome 
861*d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_LBA: %llu\n",
862*d7802caeSToomas Soome 		    *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
863*d7802caeSToomas Soome 
864*d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
865*d7802caeSToomas Soome 		    *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
866*d7802caeSToomas Soome 
867*d7802caeSToomas Soome 		uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
868*d7802caeSToomas Soome 		    uuid);
869*d7802caeSToomas Soome 
870*d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
871*d7802caeSToomas Soome 	}
872*d7802caeSToomas Soome 	printf("\n");
8730c946d80SToomas Soome }
8740c946d80SToomas Soome 
875*d7802caeSToomas Soome static void
876*d7802caeSToomas Soome print_einfo_cb(struct partlist *plist)
8770c946d80SToomas Soome {
878*d7802caeSToomas Soome 	uint8_t flags = 0;
879*d7802caeSToomas Soome 	ib_bootblock_t *bblock;
880*d7802caeSToomas Soome 	bblk_einfo_t *einfo = NULL;
881*d7802caeSToomas Soome 	const char *filepath;
882*d7802caeSToomas Soome 
883*d7802caeSToomas Soome 	/* No stage, get out. */
884*d7802caeSToomas Soome 	bblock = plist->pl_stage;
885*d7802caeSToomas Soome 	if (bblock == NULL)
886*d7802caeSToomas Soome 		return;
8870c946d80SToomas Soome 
888*d7802caeSToomas Soome 	if (plist->pl_device->stage.path == NULL)
889*d7802caeSToomas Soome 		filepath = "";
890*d7802caeSToomas Soome 	else
891*d7802caeSToomas Soome 		filepath = plist->pl_device->stage.path;
8920c946d80SToomas Soome 
893*d7802caeSToomas Soome 	printf("Boot block from %s:%s\n", plist->pl_devname, filepath);
8940c946d80SToomas Soome 
895*d7802caeSToomas Soome 	if (bblock->extra != NULL)
896*d7802caeSToomas Soome 		einfo = find_einfo(bblock->extra, bblock->extra_size);
8970c946d80SToomas Soome 
898*d7802caeSToomas Soome 	if (einfo == NULL) {
899*d7802caeSToomas Soome 		(void) fprintf(stderr,
900*d7802caeSToomas Soome 		    gettext("No extended information found.\n\n"));
901*d7802caeSToomas Soome 		return;
902*d7802caeSToomas Soome 	}
9030c946d80SToomas Soome 
904*d7802caeSToomas Soome 	/* Print the extended information. */
905*d7802caeSToomas Soome 	if (strip)
906*d7802caeSToomas Soome 		flags |= EINFO_EASY_PARSE;
907*d7802caeSToomas Soome 	if (verbose_dump)
908*d7802caeSToomas Soome 		flags |= EINFO_PRINT_HEADER;
909*d7802caeSToomas Soome 
910*d7802caeSToomas Soome 	print_einfo(flags, einfo, bblock->extra_size);
911*d7802caeSToomas Soome 	printf("\n");
9120c946d80SToomas Soome }
9130c946d80SToomas Soome 
914*d7802caeSToomas Soome static size_t
915*d7802caeSToomas Soome get_media_info(int fd)
9160c946d80SToomas Soome {
917*d7802caeSToomas Soome 	struct dk_minfo disk_info;
9180c946d80SToomas Soome 
919*d7802caeSToomas Soome 	if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
920*d7802caeSToomas Soome 		return (SECTOR_SIZE);
9210c946d80SToomas Soome 
922*d7802caeSToomas Soome 	return (disk_info.dki_lbsize);
923*d7802caeSToomas Soome }
924*d7802caeSToomas Soome 
925*d7802caeSToomas Soome static struct partlist *
926*d7802caeSToomas Soome partlist_alloc(void)
927*d7802caeSToomas Soome {
928*d7802caeSToomas Soome 	struct partlist *pl;
929*d7802caeSToomas Soome 
930*d7802caeSToomas Soome 	if ((pl = calloc(1, sizeof (*pl))) == NULL) {
931*d7802caeSToomas Soome 		perror("calloc");
932*d7802caeSToomas Soome 		return (NULL);
9330c946d80SToomas Soome 	}
9340c946d80SToomas Soome 
935*d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
936*d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
937*d7802caeSToomas Soome 		perror("calloc");
938*d7802caeSToomas Soome 		free(pl);
939*d7802caeSToomas Soome 		return (NULL);
940*d7802caeSToomas Soome 	}
941*d7802caeSToomas Soome 
942*d7802caeSToomas Soome 	return (pl);
943*d7802caeSToomas Soome }
944*d7802caeSToomas Soome 
945*d7802caeSToomas Soome static void
946*d7802caeSToomas Soome partlist_free(struct partlist *pl)
947*d7802caeSToomas Soome {
948*d7802caeSToomas Soome 	ib_bootblock_t *bblock;
949*d7802caeSToomas Soome 	ib_device_t *device;
950*d7802caeSToomas Soome 
951*d7802caeSToomas Soome 	switch (pl->pl_type) {
952*d7802caeSToomas Soome 	case IB_BBLK_MBR:
953*d7802caeSToomas Soome 	case IB_BBLK_STAGE1:
954*d7802caeSToomas Soome 		free(pl->pl_stage);
955*d7802caeSToomas Soome 		break;
956*d7802caeSToomas Soome 	default:
957*d7802caeSToomas Soome 		if (pl->pl_stage != NULL) {
958*d7802caeSToomas Soome 			bblock = pl->pl_stage;
959*d7802caeSToomas Soome 			free(bblock->buf);
960*d7802caeSToomas Soome 			free(bblock);
9610c946d80SToomas Soome 		}
9620c946d80SToomas Soome 	}
963*d7802caeSToomas Soome 
964*d7802caeSToomas Soome 	/* umount the stage fs. */
965*d7802caeSToomas Soome 	if (pl->pl_device->stage.mntpnt != NULL) {
966*d7802caeSToomas Soome 		if (umount(pl->pl_device->stage.mntpnt) == 0)
967*d7802caeSToomas Soome 			(void) rmdir(pl->pl_device->stage.mntpnt);
968*d7802caeSToomas Soome 		free(pl->pl_device->stage.mntpnt);
9690c946d80SToomas Soome 	}
970*d7802caeSToomas Soome 	device = pl->pl_device;
971*d7802caeSToomas Soome 	free(device->target.path);
972*d7802caeSToomas Soome 	free(pl->pl_device);
9730c946d80SToomas Soome 
974*d7802caeSToomas Soome 	free(pl->pl_src_data);
975*d7802caeSToomas Soome 	free(pl->pl_devname);
976*d7802caeSToomas Soome 	free(pl);
977*d7802caeSToomas Soome }
9780c946d80SToomas Soome 
979*d7802caeSToomas Soome static bool
980*d7802caeSToomas Soome probe_fstyp(ib_data_t *data)
981*d7802caeSToomas Soome {
982*d7802caeSToomas Soome 	fstyp_handle_t fhdl;
983*d7802caeSToomas Soome 	const char *fident;
984*d7802caeSToomas Soome 	char *ptr;
985*d7802caeSToomas Soome 	int fd;
986*d7802caeSToomas Soome 	bool rv = false;
987*d7802caeSToomas Soome 
988*d7802caeSToomas Soome 	/* Record partition id */
989*d7802caeSToomas Soome 	ptr = strrchr(data->target.path, 'p');
990*d7802caeSToomas Soome 	if (ptr == NULL)
991*d7802caeSToomas Soome 		ptr = strrchr(data->target.path, 's');
992*d7802caeSToomas Soome 	data->target.id = atoi(++ptr);
993*d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) == -1)
994*d7802caeSToomas Soome 		return (rv);
995*d7802caeSToomas Soome 
996*d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
997*d7802caeSToomas Soome 		(void) close(fd);
998*d7802caeSToomas Soome 		return (rv);
999*d7802caeSToomas Soome 	}
1000*d7802caeSToomas Soome 
1001*d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1002*d7802caeSToomas Soome 		fstyp_fini(fhdl);
1003*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
1004*d7802caeSToomas Soome 		    "system type\n"));
1005*d7802caeSToomas Soome 		(void) close(fd);
1006*d7802caeSToomas Soome 		return (rv);
1007*d7802caeSToomas Soome 	}
1008*d7802caeSToomas Soome 
1009*d7802caeSToomas Soome 	rv = true;
1010*d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_ZFS) == 0)
1011*d7802caeSToomas Soome 		data->target.fstype = IB_FS_ZFS;
1012*d7802caeSToomas Soome 	else if (strcmp(fident, MNTTYPE_UFS) == 0) {
1013*d7802caeSToomas Soome 		data->target.fstype = IB_FS_UFS;
1014*d7802caeSToomas Soome 	} else if (strcmp(fident, MNTTYPE_PCFS) == 0) {
1015*d7802caeSToomas Soome 		data->target.fstype = IB_FS_PCFS;
1016*d7802caeSToomas Soome 	} else {
1017*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("File system %s is not "
1018*d7802caeSToomas Soome 		    "supported by loader\n"), fident);
1019*d7802caeSToomas Soome 		rv = false;
1020*d7802caeSToomas Soome 	}
1021*d7802caeSToomas Soome 	fstyp_fini(fhdl);
1022*d7802caeSToomas Soome 	(void) close(fd);
1023*d7802caeSToomas Soome 	return (rv);
10240c946d80SToomas Soome }
10250c946d80SToomas Soome 
1026*d7802caeSToomas Soome static bool
1027*d7802caeSToomas Soome get_slice(ib_data_t *data, struct partlist *pl, struct dk_gpt *vtoc,
1028*d7802caeSToomas Soome     uint16_t tag)
10290c946d80SToomas Soome {
1030*d7802caeSToomas Soome 	uint_t i;
1031*d7802caeSToomas Soome 	ib_device_t *device = pl->pl_device;
1032*d7802caeSToomas Soome 	char *path, *ptr;
10330c946d80SToomas Soome 
1034*d7802caeSToomas Soome 	if (tag != V_BOOT && tag != V_SYSTEM)
1035*d7802caeSToomas Soome 		return (false);
10360c946d80SToomas Soome 
1037*d7802caeSToomas Soome 	for (i = 0; i < vtoc->efi_nparts; i++) {
1038*d7802caeSToomas Soome 		if (vtoc->efi_parts[i].p_tag == tag) {
1039*d7802caeSToomas Soome 			if ((path = strdup(data->target.path)) == NULL) {
1040*d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1041*d7802caeSToomas Soome 				return (false);
1042*d7802caeSToomas Soome 			}
1043*d7802caeSToomas Soome 			ptr = strrchr(path, 's');
1044*d7802caeSToomas Soome 			ptr++;
1045*d7802caeSToomas Soome 			*ptr = '\0';
1046*d7802caeSToomas Soome 			(void) asprintf(&ptr, "%s%d", path, i);
1047*d7802caeSToomas Soome 			free(path);
1048*d7802caeSToomas Soome 			if (ptr == NULL) {
1049*d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1050*d7802caeSToomas Soome 				return (false);
1051*d7802caeSToomas Soome 			}
1052*d7802caeSToomas Soome 			pl->pl_devname = ptr;
1053*d7802caeSToomas Soome 			device->stage.id = i;
1054*d7802caeSToomas Soome 			device->stage.devtype = IB_DEV_EFI;
1055*d7802caeSToomas Soome 			switch (vtoc->efi_parts[i].p_tag) {
1056*d7802caeSToomas Soome 			case V_BOOT:
1057*d7802caeSToomas Soome 				device->stage.fstype = IB_FS_NONE;
1058*d7802caeSToomas Soome 				/* leave sector 0 for VBR */
1059*d7802caeSToomas Soome 				device->stage.offset = 1;
1060*d7802caeSToomas Soome 				break;
1061*d7802caeSToomas Soome 			case V_SYSTEM:
1062*d7802caeSToomas Soome 				device->stage.fstype = IB_FS_PCFS;
1063*d7802caeSToomas Soome 				break;
1064*d7802caeSToomas Soome 			}
1065*d7802caeSToomas Soome 			device->stage.tag = vtoc->efi_parts[i].p_tag;
1066*d7802caeSToomas Soome 			device->stage.start = vtoc->efi_parts[i].p_start;
1067*d7802caeSToomas Soome 			device->stage.size = vtoc->efi_parts[i].p_size;
1068*d7802caeSToomas Soome 			break;
10690c946d80SToomas Soome 		}
10700c946d80SToomas Soome 	}
1071*d7802caeSToomas Soome 	return (true);
1072*d7802caeSToomas Soome }
10730c946d80SToomas Soome 
1074*d7802caeSToomas Soome static bool
1075*d7802caeSToomas Soome allocate_slice(ib_data_t *data, struct dk_gpt *vtoc, uint16_t tag,
1076*d7802caeSToomas Soome     struct partlist **plp)
1077*d7802caeSToomas Soome {
1078*d7802caeSToomas Soome 	struct partlist *pl;
10790c946d80SToomas Soome 
1080*d7802caeSToomas Soome 	*plp = NULL;
1081*d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1082*d7802caeSToomas Soome 		return (false);
10830c946d80SToomas Soome 
1084*d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
1085*d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
1086*d7802caeSToomas Soome 		perror("calloc");
1087*d7802caeSToomas Soome 		partlist_free(pl);
1088*d7802caeSToomas Soome 		return (false);
1089*d7802caeSToomas Soome 	}
1090*d7802caeSToomas Soome 	if (!get_slice(data, pl, vtoc, tag)) {
1091*d7802caeSToomas Soome 		partlist_free(pl);
1092*d7802caeSToomas Soome 		return (false);
10930c946d80SToomas Soome 	}
10940c946d80SToomas Soome 
1095*d7802caeSToomas Soome 	/* tag was not found */
1096*d7802caeSToomas Soome 	if (pl->pl_devname == NULL)
1097*d7802caeSToomas Soome 		partlist_free(pl);
1098*d7802caeSToomas Soome 	else
1099*d7802caeSToomas Soome 		*plp = pl;
1100*d7802caeSToomas Soome 
1101*d7802caeSToomas Soome 	return (true);
11020c946d80SToomas Soome }
11030c946d80SToomas Soome 
1104*d7802caeSToomas Soome static bool
1105*d7802caeSToomas Soome probe_gpt(ib_data_t *data)
11060c946d80SToomas Soome {
1107*d7802caeSToomas Soome 	struct partlist *pl;
1108*d7802caeSToomas Soome 	struct dk_gpt *vtoc;
1109*d7802caeSToomas Soome 	ib_device_t *device;
1110*d7802caeSToomas Soome 	int slice, fd;
1111*d7802caeSToomas Soome 	bool rv = false;
11120c946d80SToomas Soome 
1113*d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1114*d7802caeSToomas Soome 		return (rv);
11150c946d80SToomas Soome 
1116*d7802caeSToomas Soome 	slice = efi_alloc_and_read(fd, &vtoc);
1117*d7802caeSToomas Soome 	(void) close(fd);
1118*d7802caeSToomas Soome 	if (slice < 0)
1119*d7802caeSToomas Soome 		return (rv);
1120*d7802caeSToomas Soome 
1121*d7802caeSToomas Soome 	data->device.devtype = IB_DEV_EFI;
1122*d7802caeSToomas Soome 	data->target.start = vtoc->efi_parts[slice].p_start;
1123*d7802caeSToomas Soome 	data->target.size = vtoc->efi_parts[slice].p_size;
11240c946d80SToomas Soome 
1125*d7802caeSToomas Soome 	/* Always update PMBR. */
1126*d7802caeSToomas Soome 	force_mbr = 1;
1127*d7802caeSToomas Soome 	write_mbr = 1;
1128*d7802caeSToomas Soome 
1129*d7802caeSToomas Soome 	/*
1130*d7802caeSToomas Soome 	 * With GPT we can have boot partition and ESP.
1131*d7802caeSToomas Soome 	 * Boot partition can have both stage 1 and stage 2.
1132*d7802caeSToomas Soome 	 */
1133*d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1134*d7802caeSToomas Soome 		goto done;
1135*d7802caeSToomas Soome 	if (pl != NULL) {
1136*d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1137*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1138*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1139*d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1140*d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1141*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1142*d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1143*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1144*d7802caeSToomas Soome 	} else if (data->target.fstype != IB_FS_ZFS) {
1145*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Booting %s from EFI "
1146*d7802caeSToomas Soome 		    "labeled disks requires the boot partition.\n"),
1147*d7802caeSToomas Soome 		    data->target.fstype == IB_FS_UFS?
1148*d7802caeSToomas Soome 		    MNTTYPE_UFS : MNTTYPE_PCFS);
1149*d7802caeSToomas Soome 		goto done;
1150*d7802caeSToomas Soome 	}
1151*d7802caeSToomas Soome 	/* Add stage 2 */
1152*d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1153*d7802caeSToomas Soome 		goto done;
1154*d7802caeSToomas Soome 	if (pl != NULL) {
1155*d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1156*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1157*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1158*d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1159*d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1160*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1161*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1162*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1163*d7802caeSToomas Soome 	}
1164*d7802caeSToomas Soome 
1165*d7802caeSToomas Soome 	/* ESP can have 32- and 64-bit boot code. */
1166*d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1167*d7802caeSToomas Soome 		goto done;
1168*d7802caeSToomas Soome 	if (pl != NULL) {
1169*d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTIA32;
1170*d7802caeSToomas Soome 		pl->pl_src_name = efi32;
1171*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1172*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1173*d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1174*d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1175*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1176*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1177*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1178*d7802caeSToomas Soome 	}
1179*d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1180*d7802caeSToomas Soome 		goto done;
1181*d7802caeSToomas Soome 	if (pl != NULL) {
1182*d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTX64;
1183*d7802caeSToomas Soome 		pl->pl_src_name = efi64;
1184*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1185*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1186*d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1187*d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1188*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1189*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1190*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1191*d7802caeSToomas Soome 	}
1192*d7802caeSToomas Soome 
1193*d7802caeSToomas Soome 	/* add stage for our target file system slice */
1194*d7802caeSToomas Soome 	pl = partlist_alloc();
1195*d7802caeSToomas Soome 	if (pl == NULL)
1196*d7802caeSToomas Soome 		goto done;
1197*d7802caeSToomas Soome 
1198*d7802caeSToomas Soome 	device = pl->pl_device;
1199*d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1200*d7802caeSToomas Soome 	if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1201*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1202*d7802caeSToomas Soome 		partlist_free(pl);
1203*d7802caeSToomas Soome 		goto done;
12040c946d80SToomas Soome 	}
12050c946d80SToomas Soome 
1206*d7802caeSToomas Soome 	device->stage.id = slice;
1207*d7802caeSToomas Soome 	device->stage.start = vtoc->efi_parts[slice].p_start;
1208*d7802caeSToomas Soome 	device->stage.size = vtoc->efi_parts[slice].p_size;
12090c946d80SToomas Soome 
1210*d7802caeSToomas Soome 	/* ZFS and UFS can have stage1 in boot area. */
1211*d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS ||
1212*d7802caeSToomas Soome 	    data->target.fstype == IB_FS_UFS) {
1213*d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1214*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1215*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1216*d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1217*d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1218*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1219*d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1220*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1221*d7802caeSToomas Soome 	}
12220c946d80SToomas Soome 
1223*d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS) {
1224*d7802caeSToomas Soome 		pl = partlist_alloc();
1225*d7802caeSToomas Soome 		if (pl == NULL)
1226*d7802caeSToomas Soome 			goto done;
12270c946d80SToomas Soome 
1228*d7802caeSToomas Soome 		device = pl->pl_device;
1229*d7802caeSToomas Soome 		device->stage.devtype = data->device.devtype;
1230*d7802caeSToomas Soome 
1231*d7802caeSToomas Soome 		if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1232*d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1233*d7802caeSToomas Soome 			goto done;
12340c946d80SToomas Soome 		}
12350c946d80SToomas Soome 
1236*d7802caeSToomas Soome 		device->stage.id = slice;
1237*d7802caeSToomas Soome 		device->stage.start = vtoc->efi_parts[slice].p_start;
1238*d7802caeSToomas Soome 		device->stage.size = vtoc->efi_parts[slice].p_size;
1239*d7802caeSToomas Soome 
1240*d7802caeSToomas Soome 		device->stage.offset = BBLK_ZFS_BLK_OFF;
1241*d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1242*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1243*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1244*d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1245*d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1246*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1247*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1248*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1249*d7802caeSToomas Soome 	}
1250*d7802caeSToomas Soome 	rv = true;
1251*d7802caeSToomas Soome done:
1252*d7802caeSToomas Soome 	efi_free(vtoc);
1253*d7802caeSToomas Soome 	return (rv);
1254*d7802caeSToomas Soome }
12550c946d80SToomas Soome 
1256*d7802caeSToomas Soome static bool
1257*d7802caeSToomas Soome get_start_sector(ib_data_t *data, struct extpartition *v_part,
1258*d7802caeSToomas Soome     diskaddr_t *start)
1259*d7802caeSToomas Soome {
1260*d7802caeSToomas Soome 	struct partlist *pl;
1261*d7802caeSToomas Soome 	struct mboot *mbr;
1262*d7802caeSToomas Soome 	struct ipart *part;
1263*d7802caeSToomas Soome 	struct part_info dkpi;
1264*d7802caeSToomas Soome 	struct extpart_info edkpi;
1265*d7802caeSToomas Soome 	uint32_t secnum, numsec;
1266*d7802caeSToomas Soome 	ext_part_t *epp;
1267*d7802caeSToomas Soome 	ushort_t i;
1268*d7802caeSToomas Soome 	int fd, rval, pno;
1269*d7802caeSToomas Soome 
1270*d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1271*d7802caeSToomas Soome 		return (false);
1272*d7802caeSToomas Soome 
1273*d7802caeSToomas Soome 	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
1274*d7802caeSToomas Soome 		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
12750c946d80SToomas Soome 			(void) fprintf(stderr, gettext("cannot get the "
12760c946d80SToomas Soome 			    "slice information of the disk\n"));
1277*d7802caeSToomas Soome 			(void) close(fd);
1278*d7802caeSToomas Soome 			return (false);
12790c946d80SToomas Soome 		} else {
12800c946d80SToomas Soome 			edkpi.p_start = dkpi.p_start;
12810c946d80SToomas Soome 			edkpi.p_length = dkpi.p_length;
12820c946d80SToomas Soome 		}
12830c946d80SToomas Soome 	}
1284*d7802caeSToomas Soome 	(void) close(fd);
12850c946d80SToomas Soome 
1286*d7802caeSToomas Soome 	/* Set target file system start and size */
1287*d7802caeSToomas Soome 	data->target.start = edkpi.p_start;
1288*d7802caeSToomas Soome 	data->target.size = edkpi.p_length;
12890c946d80SToomas Soome 
1290*d7802caeSToomas Soome 	/* This is our MBR partition start. */
1291*d7802caeSToomas Soome 	edkpi.p_start -= v_part->p_start;
12920c946d80SToomas Soome 
1293*d7802caeSToomas Soome 	/* Head is always MBR */
1294*d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1295*d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1296*d7802caeSToomas Soome 		return (false);
12970c946d80SToomas Soome 
1298*d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1299*d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
13000c946d80SToomas Soome 
1301*d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1302*d7802caeSToomas Soome 		if (part[i].relsect == edkpi.p_start) {
1303*d7802caeSToomas Soome 			*start = part[i].relsect;
1304*d7802caeSToomas Soome 			return (true);
1305*d7802caeSToomas Soome 		}
13060c946d80SToomas Soome 	}
13070c946d80SToomas Soome 
1308*d7802caeSToomas Soome 	rval = libfdisk_init(&epp, pl->pl_devname, part, FDISK_READ_DISK);
1309*d7802caeSToomas Soome 	if (rval != FDISK_SUCCESS) {
13100c946d80SToomas Soome 		switch (rval) {
13110c946d80SToomas Soome 			/*
13120c946d80SToomas Soome 			 * The first 3 cases are not an error per-se, just that
13130c946d80SToomas Soome 			 * there is no Solaris logical partition
13140c946d80SToomas Soome 			 */
13150c946d80SToomas Soome 			case FDISK_EBADLOGDRIVE:
13160c946d80SToomas Soome 			case FDISK_ENOLOGDRIVE:
13170c946d80SToomas Soome 			case FDISK_EBADMAGIC:
13180c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Solaris "
13190c946d80SToomas Soome 				    "partition not found. "
1320*d7802caeSToomas Soome 				    "Aborting operation. %d\n"), rval);
1321*d7802caeSToomas Soome 				return (false);
13220c946d80SToomas Soome 			case FDISK_ENOVGEOM:
13230c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13240c946d80SToomas Soome 				    "virtual geometry\n"));
1325*d7802caeSToomas Soome 				return (false);
13260c946d80SToomas Soome 			case FDISK_ENOPGEOM:
13270c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13280c946d80SToomas Soome 				    "physical geometry\n"));
1329*d7802caeSToomas Soome 				return (false);
13300c946d80SToomas Soome 			case FDISK_ENOLGEOM:
13310c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13320c946d80SToomas Soome 				    "label geometry\n"));
1333*d7802caeSToomas Soome 				return (false);
13340c946d80SToomas Soome 			default:
13350c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Failed to "
13360c946d80SToomas Soome 				    "initialize libfdisk.\n"));
1337*d7802caeSToomas Soome 				return (false);
13380c946d80SToomas Soome 		}
13390c946d80SToomas Soome 	}
13400c946d80SToomas Soome 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
13410c946d80SToomas Soome 	libfdisk_fini(&epp);
13420c946d80SToomas Soome 	if (rval != FDISK_SUCCESS) {
13430c946d80SToomas Soome 		/* No solaris logical partition */
13440c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Solaris partition not found. "
13450c946d80SToomas Soome 		    "Aborting operation.\n"));
1346*d7802caeSToomas Soome 		return (false);
13470c946d80SToomas Soome 	}
1348*d7802caeSToomas Soome 	*start = secnum;
1349*d7802caeSToomas Soome 	return (true);
1350*d7802caeSToomas Soome }
13510c946d80SToomas Soome 
1352*d7802caeSToomas Soome /*
1353*d7802caeSToomas Soome  * On x86 the VTOC table is inside MBR partition and to get
1354*d7802caeSToomas Soome  * absolute sectors, we need to add MBR partition start to VTOC slice start.
1355*d7802caeSToomas Soome  */
1356*d7802caeSToomas Soome static bool
1357*d7802caeSToomas Soome probe_vtoc(ib_data_t *data)
1358*d7802caeSToomas Soome {
1359*d7802caeSToomas Soome 	struct partlist *pl;
1360*d7802caeSToomas Soome 	struct extvtoc exvtoc;
1361*d7802caeSToomas Soome 	ib_device_t *device;
1362*d7802caeSToomas Soome 	char *path, *ptr;
1363*d7802caeSToomas Soome 	ushort_t i;
1364*d7802caeSToomas Soome 	int slice, fd;
1365*d7802caeSToomas Soome 	diskaddr_t start;
1366*d7802caeSToomas Soome 	bool rv;
13670c946d80SToomas Soome 
1368*d7802caeSToomas Soome 	rv = false;
1369*d7802caeSToomas Soome 
1370*d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1371*d7802caeSToomas Soome 		return (rv);
1372*d7802caeSToomas Soome 
1373*d7802caeSToomas Soome 	slice = read_extvtoc(fd, &exvtoc);
1374*d7802caeSToomas Soome 	(void) close(fd);
1375*d7802caeSToomas Soome 	if (slice < 0)
1376*d7802caeSToomas Soome 		return (rv);
1377*d7802caeSToomas Soome 	data->device.devtype = IB_DEV_VTOC;
1378*d7802caeSToomas Soome 
1379*d7802caeSToomas Soome 	if (!get_start_sector(data, exvtoc.v_part + slice, &start))
1380*d7802caeSToomas Soome 		return (rv);
1381*d7802caeSToomas Soome 
1382*d7802caeSToomas Soome 	if (exvtoc.v_part[slice].p_tag == V_BACKUP) {
1383*d7802caeSToomas Soome 		/*
1384*d7802caeSToomas Soome 		 * NOTE: we could relax there and allow zfs boot on
1385*d7802caeSToomas Soome 		 * slice 2, but lets keep traditional limits.
1386*d7802caeSToomas Soome 		 */
1387*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext(
1388*d7802caeSToomas Soome 		    "raw device must be a root slice (not backup)\n"));
1389*d7802caeSToomas Soome 		return (rv);
1390*d7802caeSToomas Soome 	}
1391*d7802caeSToomas Soome 
1392*d7802caeSToomas Soome 	if ((path = strdup(data->target.path)) == NULL) {
1393*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1394*d7802caeSToomas Soome 		return (false);
1395*d7802caeSToomas Soome 	}
1396*d7802caeSToomas Soome 
1397*d7802caeSToomas Soome 	data->target.start = start + exvtoc.v_part[slice].p_start;
1398*d7802caeSToomas Soome 	data->target.size = exvtoc.v_part[slice].p_size;
1399*d7802caeSToomas Soome 
1400*d7802caeSToomas Soome 	/* Search for boot slice. */
1401*d7802caeSToomas Soome 	for (i = 0; i < exvtoc.v_nparts; i++) {
1402*d7802caeSToomas Soome 		if (exvtoc.v_part[i].p_tag == V_BOOT)
1403*d7802caeSToomas Soome 			break;
1404*d7802caeSToomas Soome 	}
1405*d7802caeSToomas Soome 
1406*d7802caeSToomas Soome 	if (i == exvtoc.v_nparts ||
1407*d7802caeSToomas Soome 	    exvtoc.v_part[i].p_size == 0) {
1408*d7802caeSToomas Soome 		/* fall back to slice V_BACKUP */
1409*d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1410*d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_tag == V_BACKUP)
1411*d7802caeSToomas Soome 				break;
1412*d7802caeSToomas Soome 		}
1413*d7802caeSToomas Soome 		/* Still nothing? Error out. */
1414*d7802caeSToomas Soome 		if (i == exvtoc.v_nparts ||
1415*d7802caeSToomas Soome 		    exvtoc.v_part[i].p_size == 0) {
1416*d7802caeSToomas Soome 			free(path);
1417*d7802caeSToomas Soome 			return (false);
1418*d7802caeSToomas Soome 		}
1419*d7802caeSToomas Soome 	}
1420*d7802caeSToomas Soome 
1421*d7802caeSToomas Soome 	/* Create path. */
1422*d7802caeSToomas Soome 	ptr = strrchr(path, 's');
1423*d7802caeSToomas Soome 	ptr++;
1424*d7802caeSToomas Soome 	*ptr = '\0';
1425*d7802caeSToomas Soome 	(void) asprintf(&ptr, "%s%d", path, i);
1426*d7802caeSToomas Soome 	free(path);
1427*d7802caeSToomas Soome 	if (ptr == NULL) {
1428*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1429*d7802caeSToomas Soome 		return (false);
1430*d7802caeSToomas Soome 	}
1431*d7802caeSToomas Soome 
1432*d7802caeSToomas Soome 	pl = partlist_alloc();
1433*d7802caeSToomas Soome 	if (pl == NULL) {
1434*d7802caeSToomas Soome 		free(ptr);
1435*d7802caeSToomas Soome 		return (false);
1436*d7802caeSToomas Soome 	}
1437*d7802caeSToomas Soome 	pl->pl_devname = ptr;
1438*d7802caeSToomas Soome 	device = pl->pl_device;
1439*d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1440*d7802caeSToomas Soome 	device->stage.id = i;
1441*d7802caeSToomas Soome 	device->stage.tag = exvtoc.v_part[i].p_tag;
1442*d7802caeSToomas Soome 	device->stage.start = start + exvtoc.v_part[i].p_start;
1443*d7802caeSToomas Soome 	device->stage.size = exvtoc.v_part[i].p_size;
1444*d7802caeSToomas Soome 
1445*d7802caeSToomas Soome 	/* Fix size if this slice is in fact V_BACKUP */
1446*d7802caeSToomas Soome 	if (exvtoc.v_part[i].p_tag == V_BACKUP) {
1447*d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1448*d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start == 0)
1449*d7802caeSToomas Soome 				continue;
1450*d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_size == 0)
1451*d7802caeSToomas Soome 				continue;
1452*d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start <
1453*d7802caeSToomas Soome 			    device->stage.size)
1454*d7802caeSToomas Soome 				device->stage.size =
1455*d7802caeSToomas Soome 				    exvtoc.v_part[i].p_start;
14560c946d80SToomas Soome 		}
14570c946d80SToomas Soome 	}
14580c946d80SToomas Soome 
1459*d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1460*d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1461*d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1462*d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1463*d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1464*d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1465*d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1466*d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1467*d7802caeSToomas Soome 
1468*d7802caeSToomas Soome 	/* Create instance for stage 2 */
1469*d7802caeSToomas Soome 	pl = partlist_alloc();
1470*d7802caeSToomas Soome 	if (pl == NULL) {
1471*d7802caeSToomas Soome 		free(ptr);
1472*d7802caeSToomas Soome 		return (false);
1473*d7802caeSToomas Soome 	}
1474*d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1475*d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1476*d7802caeSToomas Soome 		partlist_free(pl);
1477*d7802caeSToomas Soome 		return (false);
1478*d7802caeSToomas Soome 	}
1479*d7802caeSToomas Soome 	pl->pl_device->stage.devtype = data->device.devtype;
1480*d7802caeSToomas Soome 	pl->pl_device->stage.id = device->stage.id;
1481*d7802caeSToomas Soome 	pl->pl_device->stage.offset = BBLK_BLKLIST_OFF;
1482*d7802caeSToomas Soome 	pl->pl_device->stage.tag = device->stage.tag;
1483*d7802caeSToomas Soome 	pl->pl_device->stage.start = device->stage.start;
1484*d7802caeSToomas Soome 	pl->pl_device->stage.size = device->stage.size;
1485*d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1486*d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1487*d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1488*d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1489*d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1490*d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1491*d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1492*d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1493*d7802caeSToomas Soome 
1494*d7802caeSToomas Soome 	/* And we are done. */
1495*d7802caeSToomas Soome 	rv = true;
1496*d7802caeSToomas Soome 	return (rv);
1497*d7802caeSToomas Soome }
1498*d7802caeSToomas Soome 
1499*d7802caeSToomas Soome static bool
1500*d7802caeSToomas Soome probe_mbr(ib_data_t *data)
1501*d7802caeSToomas Soome {
1502*d7802caeSToomas Soome 	struct partlist *pl;
1503*d7802caeSToomas Soome 	struct ipart *part;
1504*d7802caeSToomas Soome 	struct mboot *mbr;
1505*d7802caeSToomas Soome 	ib_device_t *device;
1506*d7802caeSToomas Soome 	char *path, *ptr;
1507*d7802caeSToomas Soome 	int i, rv;
1508*d7802caeSToomas Soome 
1509*d7802caeSToomas Soome 	data->device.devtype = IB_DEV_MBR;
1510*d7802caeSToomas Soome 
1511*d7802caeSToomas Soome 	/* Head is always MBR */
1512*d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1513*d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1514*d7802caeSToomas Soome 		return (false);
1515*d7802caeSToomas Soome 
1516*d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1517*d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
1518*d7802caeSToomas Soome 
1519*d7802caeSToomas Soome 	/* Set target file system start and size */
1520*d7802caeSToomas Soome 	data->target.start = part[data->target.id - 1].relsect;
1521*d7802caeSToomas Soome 	data->target.size = part[data->target.id - 1].numsect;
1522*d7802caeSToomas Soome 
1523*d7802caeSToomas Soome 	/* Use X86BOOT partition if we have one. */
1524*d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1525*d7802caeSToomas Soome 		if (part[i].systid == X86BOOT)
1526*d7802caeSToomas Soome 			break;
1527*d7802caeSToomas Soome 	}
1528*d7802caeSToomas Soome 
1529*d7802caeSToomas Soome 	/* Keep device name of whole disk device. */
1530*d7802caeSToomas Soome 	path = (char *)pl->pl_devname;
1531*d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1532*d7802caeSToomas Soome 		return (false);
1533*d7802caeSToomas Soome 	device = pl->pl_device;
1534*d7802caeSToomas Soome 
15350c946d80SToomas Soome 	/*
1536*d7802caeSToomas Soome 	 * No X86BOOT, try to use space between MBR and first
1537*d7802caeSToomas Soome 	 * partition.
15380c946d80SToomas Soome 	 */
1539*d7802caeSToomas Soome 	if (i == FD_NUMPART) {
1540*d7802caeSToomas Soome 		/* with pcfs we always write MBR */
1541*d7802caeSToomas Soome 		if (data->target.fstype == IB_FS_PCFS) {
1542*d7802caeSToomas Soome 			force_mbr = true;
1543*d7802caeSToomas Soome 			write_mbr = true;
15440c946d80SToomas Soome 		}
1545*d7802caeSToomas Soome 
1546*d7802caeSToomas Soome 		pl->pl_devname = strdup(path);
1547*d7802caeSToomas Soome 		if (pl->pl_devname == NULL) {
1548*d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1549*d7802caeSToomas Soome 			partlist_free(pl);
1550*d7802caeSToomas Soome 			return (false);
1551*d7802caeSToomas Soome 		}
1552*d7802caeSToomas Soome 		device->stage.id = 0;
1553*d7802caeSToomas Soome 		device->stage.devtype = IB_DEV_MBR;
1554*d7802caeSToomas Soome 		device->stage.fstype = IB_FS_NONE;
1555*d7802caeSToomas Soome 		device->stage.start = 0;
1556*d7802caeSToomas Soome 		device->stage.size = part[0].relsect;
1557*d7802caeSToomas Soome 		device->stage.offset = BBLK_BLKLIST_OFF;
1558*d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1559*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1560*d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1561*d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1562*d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1563*d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1564*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1565*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1566*d7802caeSToomas Soome 
1567*d7802caeSToomas Soome 		/* We have MBR for stage1 and gap for stage2, we are done. */
1568*d7802caeSToomas Soome 		return (true);
15690c946d80SToomas Soome 	}
15700c946d80SToomas Soome 
1571*d7802caeSToomas Soome 	if ((path = strdup(path)) == NULL) {
1572*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1573*d7802caeSToomas Soome 		partlist_free(pl);
1574*d7802caeSToomas Soome 		return (false);
1575*d7802caeSToomas Soome 	}
1576*d7802caeSToomas Soome 	ptr = strrchr(path, 'p');
1577*d7802caeSToomas Soome 	ptr++;
1578*d7802caeSToomas Soome 	*ptr = '\0';
1579*d7802caeSToomas Soome 	/* partitions are p1..p4 */
1580*d7802caeSToomas Soome 	rv = asprintf(&ptr, "%s%d", path, i + 1);
1581*d7802caeSToomas Soome 	free(path);
1582*d7802caeSToomas Soome 	if (rv < 0) {
1583*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1584*d7802caeSToomas Soome 		partlist_free(pl);
1585*d7802caeSToomas Soome 		return (false);
1586*d7802caeSToomas Soome 	}
1587*d7802caeSToomas Soome 	pl->pl_devname = ptr;
1588*d7802caeSToomas Soome 	device->stage.id = i + 1;
1589*d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1590*d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1591*d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1592*d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1593*d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1594*d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1595*d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1596*d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1597*d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1598*d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1599*d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1600*d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1601*d7802caeSToomas Soome 
1602*d7802caeSToomas Soome 	pl = partlist_alloc();
1603*d7802caeSToomas Soome 	if (pl == NULL)
1604*d7802caeSToomas Soome 		return (false);
1605*d7802caeSToomas Soome 	device = pl->pl_device;
1606*d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1607*d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1608*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1609*d7802caeSToomas Soome 		partlist_free(pl);
1610*d7802caeSToomas Soome 		return (false);
1611*d7802caeSToomas Soome 	}
1612*d7802caeSToomas Soome 	device->stage.id = i + 1;
1613*d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1614*d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1615*d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1616*d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1617*d7802caeSToomas Soome 	device->stage.offset = 1;
1618*d7802caeSToomas Soome 	/* This is boot partition */
1619*d7802caeSToomas Soome 	device->stage.tag = V_BOOT;
1620*d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1621*d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1622*d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1623*d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1624*d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1625*d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1626*d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1627*d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1628*d7802caeSToomas Soome 
1629*d7802caeSToomas Soome 	return (true);
1630*d7802caeSToomas Soome }
1631*d7802caeSToomas Soome 
1632*d7802caeSToomas Soome static bool
1633*d7802caeSToomas Soome probe_device(ib_data_t *data, const char *dev)
1634*d7802caeSToomas Soome {
1635*d7802caeSToomas Soome 	struct partlist *pl;
1636*d7802caeSToomas Soome 	struct stat sb;
1637*d7802caeSToomas Soome 	const char *ptr;
1638*d7802caeSToomas Soome 	char *p0;
1639*d7802caeSToomas Soome 	int fd, len;
1640*d7802caeSToomas Soome 
1641*d7802caeSToomas Soome 	if (dev == NULL)
1642*d7802caeSToomas Soome 		return (NULL);
1643*d7802caeSToomas Soome 
1644*d7802caeSToomas Soome 	len = strlen(dev);
1645*d7802caeSToomas Soome 
1646*d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1647*d7802caeSToomas Soome 		return (false);
1648*d7802caeSToomas Soome 
1649*d7802caeSToomas Soome 	if (stat(dev, &sb) == -1) {
1650*d7802caeSToomas Soome 		perror("stat");
1651*d7802caeSToomas Soome 		partlist_free(pl);
1652*d7802caeSToomas Soome 		return (false);
1653*d7802caeSToomas Soome 	}
1654*d7802caeSToomas Soome 
1655*d7802caeSToomas Soome 	/* We have regular file, register it and we are done. */
1656*d7802caeSToomas Soome 	if (S_ISREG(sb.st_mode) != 0) {
1657*d7802caeSToomas Soome 		pl->pl_devname = (char *)dev;
1658*d7802caeSToomas Soome 
1659*d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_FILE;
1660*d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_file_cb;
1661*d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1662*d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1663*d7802caeSToomas Soome 		return (true);
1664*d7802caeSToomas Soome 	}
1665*d7802caeSToomas Soome 
1666*d7802caeSToomas Soome 	/*
1667*d7802caeSToomas Soome 	 * This is block device.
1668*d7802caeSToomas Soome 	 * We do not allow to specify whole disk device (cXtYdZp0 or cXtYdZ).
1669*d7802caeSToomas Soome 	 */
1670*d7802caeSToomas Soome 	if ((ptr = strrchr(dev, '/')) == NULL)
1671*d7802caeSToomas Soome 		ptr = dev;
1672*d7802caeSToomas Soome 	if ((strrchr(ptr, 'p') == NULL && strrchr(ptr, 's') == NULL) ||
1673*d7802caeSToomas Soome 	    (dev[len - 2] == 'p' && dev[len - 1] == '0')) {
1674*d7802caeSToomas Soome 		(void) fprintf(stderr,
1675*d7802caeSToomas Soome 		    gettext("whole disk device is not supported\n"));
1676*d7802caeSToomas Soome 		partlist_free(pl);
1677*d7802caeSToomas Soome 		return (false);
1678*d7802caeSToomas Soome 	}
1679*d7802caeSToomas Soome 
1680*d7802caeSToomas Soome 	data->target.path = (char *)dev;
1681*d7802caeSToomas Soome 	if (!probe_fstyp(data)) {
1682*d7802caeSToomas Soome 		partlist_free(pl);
1683*d7802caeSToomas Soome 		return (false);
1684*d7802caeSToomas Soome 	}
1685*d7802caeSToomas Soome 
1686*d7802caeSToomas Soome 	/* We start from identifying the whole disk. */
1687*d7802caeSToomas Soome 	if ((p0 = strdup(dev)) == NULL) {
1688*d7802caeSToomas Soome 		perror("calloc");
1689*d7802caeSToomas Soome 		partlist_free(pl);
1690*d7802caeSToomas Soome 		return (false);
1691*d7802caeSToomas Soome 	}
1692*d7802caeSToomas Soome 
1693*d7802caeSToomas Soome 	pl->pl_devname = p0;
1694*d7802caeSToomas Soome 	/* Change device name to p0 */
1695*d7802caeSToomas Soome 	if ((ptr = strrchr(p0, 'p')) == NULL)
1696*d7802caeSToomas Soome 		ptr = strrchr(p0, 's');
1697*d7802caeSToomas Soome 	p0 = (char *)ptr;
1698*d7802caeSToomas Soome 	p0[0] = 'p';
1699*d7802caeSToomas Soome 	p0[1] = '0';
1700*d7802caeSToomas Soome 	p0[2] = '\0';
1701*d7802caeSToomas Soome 
1702*d7802caeSToomas Soome 	if ((fd = open_device(pl->pl_devname)) == -1) {
1703*d7802caeSToomas Soome 		partlist_free(pl);
1704*d7802caeSToomas Soome 		return (false);
1705*d7802caeSToomas Soome 	}
1706*d7802caeSToomas Soome 
1707*d7802caeSToomas Soome 	sector_size = get_media_info(fd);
1708*d7802caeSToomas Soome 	(void) close(fd);
1709*d7802caeSToomas Soome 
1710*d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1711*d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_MBR;
1712*d7802caeSToomas Soome 	pl->pl_cb.compare = compare_mbr_cb;
1713*d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1714*d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1715*d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1716*d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1717*d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1718*d7802caeSToomas Soome 
1719*d7802caeSToomas Soome 	if (probe_gpt(data))
1720*d7802caeSToomas Soome 		return (true);
1721*d7802caeSToomas Soome 
1722*d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1723*d7802caeSToomas Soome 		if (probe_vtoc(data))
1724*d7802caeSToomas Soome 			return (true);
1725*d7802caeSToomas Soome 
1726*d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1727*d7802caeSToomas Soome 		return (probe_mbr(data));
1728*d7802caeSToomas Soome 
1729*d7802caeSToomas Soome 	return (false);
1730*d7802caeSToomas Soome }
1731*d7802caeSToomas Soome 
1732*d7802caeSToomas Soome static int
1733*d7802caeSToomas Soome read_bootblock_from_file(const char *file, ib_bootblock_t *bblock)
1734*d7802caeSToomas Soome {
1735*d7802caeSToomas Soome 	struct stat	sb;
1736*d7802caeSToomas Soome 	uint32_t	buf_size;
1737*d7802caeSToomas Soome 	uint32_t	mboot_off;
1738*d7802caeSToomas Soome 	int		fd = -1;
1739*d7802caeSToomas Soome 	int		retval = BC_ERROR;
1740*d7802caeSToomas Soome 
1741*d7802caeSToomas Soome 	assert(bblock != NULL);
1742*d7802caeSToomas Soome 	assert(file != NULL);
1743*d7802caeSToomas Soome 
1744*d7802caeSToomas Soome 	fd = open(file, O_RDONLY);
1745*d7802caeSToomas Soome 	if (fd == -1) {
1746*d7802caeSToomas Soome 		BOOT_DEBUG("Error opening %s\n", file);
1747*d7802caeSToomas Soome 		goto out;
1748*d7802caeSToomas Soome 	}
1749*d7802caeSToomas Soome 
1750*d7802caeSToomas Soome 	if (fstat(fd, &sb) == -1) {
1751*d7802caeSToomas Soome 		BOOT_DEBUG("Error getting information (stat) about %s", file);
1752*d7802caeSToomas Soome 		perror("stat");
1753*d7802caeSToomas Soome 		goto outfd;
1754*d7802caeSToomas Soome 	}
1755*d7802caeSToomas Soome 
1756*d7802caeSToomas Soome 	/* loader bootblock has version built in */
1757*d7802caeSToomas Soome 	buf_size = sb.st_size;
1758*d7802caeSToomas Soome 
1759*d7802caeSToomas Soome 	bblock->buf_size = buf_size;
1760*d7802caeSToomas Soome 	BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
1761*d7802caeSToomas Soome 	    bblock->buf_size);
1762*d7802caeSToomas Soome 
1763*d7802caeSToomas Soome 	bblock->buf = malloc(buf_size);
1764*d7802caeSToomas Soome 	if (bblock->buf == NULL) {
1765*d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1766*d7802caeSToomas Soome 		goto outbuf;
1767*d7802caeSToomas Soome 	}
1768*d7802caeSToomas Soome 	bblock->file = bblock->buf;
1769*d7802caeSToomas Soome 
1770*d7802caeSToomas Soome 	if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) {
1771*d7802caeSToomas Soome 		BOOT_DEBUG("Read from %s failed\n", file);
1772*d7802caeSToomas Soome 		perror("read");
1773*d7802caeSToomas Soome 		goto outfd;
1774*d7802caeSToomas Soome 	}
1775*d7802caeSToomas Soome 
1776*d7802caeSToomas Soome 	buf_size = MIN(buf_size, MBOOT_SCAN_SIZE);
1777*d7802caeSToomas Soome 	if (find_multiboot(bblock->file, buf_size, &mboot_off)
1778*d7802caeSToomas Soome 	    != BC_SUCCESS) {
1779*d7802caeSToomas Soome 		(void) fprintf(stderr,
1780*d7802caeSToomas Soome 		    gettext("Unable to find multiboot header\n"));
1781*d7802caeSToomas Soome 		goto outfd;
1782*d7802caeSToomas Soome 	}
1783*d7802caeSToomas Soome 
1784*d7802caeSToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
1785*d7802caeSToomas Soome 	bblock->mboot_off = mboot_off;
1786*d7802caeSToomas Soome 
1787*d7802caeSToomas Soome 	bblock->file_size =
1788*d7802caeSToomas Soome 	    bblock->mboot->load_end_addr - bblock->mboot->load_addr;
1789*d7802caeSToomas Soome 	BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
1790*d7802caeSToomas Soome 
1791*d7802caeSToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
1792*d7802caeSToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1793*d7802caeSToomas Soome 
1794*d7802caeSToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
1795*d7802caeSToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
1796*d7802caeSToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
1797*d7802caeSToomas Soome 
1798*d7802caeSToomas Soome 	(void) close(fd);
17990c946d80SToomas Soome 	return (BC_SUCCESS);
1800*d7802caeSToomas Soome 
1801*d7802caeSToomas Soome outbuf:
1802*d7802caeSToomas Soome 	(void) free(bblock->buf);
1803*d7802caeSToomas Soome 	bblock->buf = NULL;
1804*d7802caeSToomas Soome outfd:
1805*d7802caeSToomas Soome 	(void) close(fd);
1806*d7802caeSToomas Soome out:
1807*d7802caeSToomas Soome 	if (retval == BC_ERROR) {
1808*d7802caeSToomas Soome 		(void) fprintf(stderr,
1809*d7802caeSToomas Soome 		    gettext("Error reading bootblock from %s\n"),
1810*d7802caeSToomas Soome 		    file);
1811*d7802caeSToomas Soome 	}
1812*d7802caeSToomas Soome 
1813*d7802caeSToomas Soome 	if (retval == BC_NOEXTRA) {
1814*d7802caeSToomas Soome 		BOOT_DEBUG("No multiboot header found on %s, unable to "
1815*d7802caeSToomas Soome 		    "locate extra information area (old/non versioned "
1816*d7802caeSToomas Soome 		    "bootblock?) \n", file);
1817*d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("No extended information"
1818*d7802caeSToomas Soome 		    " found\n"));
1819*d7802caeSToomas Soome 	}
1820*d7802caeSToomas Soome 	return (retval);
1821*d7802caeSToomas Soome }
1822*d7802caeSToomas Soome 
1823*d7802caeSToomas Soome static void
1824*d7802caeSToomas Soome add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
1825*d7802caeSToomas Soome {
1826*d7802caeSToomas Soome 	bblk_hs_t	hs;
1827*d7802caeSToomas Soome 	uint32_t	avail_space;
1828*d7802caeSToomas Soome 
1829*d7802caeSToomas Soome 	assert(bblock != NULL);
1830*d7802caeSToomas Soome 
1831*d7802caeSToomas Soome 	if (updt_str == NULL) {
1832*d7802caeSToomas Soome 		BOOT_DEBUG("WARNING: no update string passed to "
1833*d7802caeSToomas Soome 		    "add_bootblock_einfo()\n");
1834*d7802caeSToomas Soome 		return;
1835*d7802caeSToomas Soome 	}
1836*d7802caeSToomas Soome 
1837*d7802caeSToomas Soome 	/* Fill bootblock hashing source information. */
1838*d7802caeSToomas Soome 	hs.src_buf = (unsigned char *)bblock->file;
1839*d7802caeSToomas Soome 	hs.src_size = bblock->file_size;
1840*d7802caeSToomas Soome 	/* How much space for the extended information structure? */
1841*d7802caeSToomas Soome 	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1842*d7802caeSToomas Soome 	/* Place the extended information structure. */
1843*d7802caeSToomas Soome 	add_einfo(bblock->extra, updt_str, &hs, avail_space);
1844*d7802caeSToomas Soome }
1845*d7802caeSToomas Soome 
1846*d7802caeSToomas Soome /*
1847*d7802caeSToomas Soome  * set up data for case stage1 is installed as MBR
1848*d7802caeSToomas Soome  * set up location and size of bootblock
1849*d7802caeSToomas Soome  * set disk guid to provide unique information for biosdev command
1850*d7802caeSToomas Soome  */
1851*d7802caeSToomas Soome static void
1852*d7802caeSToomas Soome prepare_stage1(struct partlist *stage1, struct partlist *stage2, uuid_t uuid)
1853*d7802caeSToomas Soome {
1854*d7802caeSToomas Soome 	char *src, *dest;
1855*d7802caeSToomas Soome 	ib_bootblock_t *bblk;
1856*d7802caeSToomas Soome 	ib_device_t *device;
1857*d7802caeSToomas Soome 	uint16_t size;
1858*d7802caeSToomas Soome 	struct mboot *mbr;
1859*d7802caeSToomas Soome 
1860*d7802caeSToomas Soome 	src = stage1->pl_stage;
1861*d7802caeSToomas Soome 	dest = stage1->pl_src_data;
1862*d7802caeSToomas Soome 	device = stage2->pl_device;
1863*d7802caeSToomas Soome 
1864*d7802caeSToomas Soome 	/* Only copy from valid source. */
1865*d7802caeSToomas Soome 	mbr = stage1->pl_stage;
1866*d7802caeSToomas Soome 	if (mbr->signature == MBB_MAGIC) {
1867*d7802caeSToomas Soome 		/* copy BPB */
1868*d7802caeSToomas Soome 		bcopy(src + STAGE1_BPB_OFFSET, dest + STAGE1_BPB_OFFSET,
1869*d7802caeSToomas Soome 		    STAGE1_BPB_SIZE);
1870*d7802caeSToomas Soome 
1871*d7802caeSToomas Soome 		/* copy MBR, note STAGE1_SIG == BOOTSZ */
1872*d7802caeSToomas Soome 		bcopy(src + STAGE1_SIG, dest + STAGE1_SIG,
1873*d7802caeSToomas Soome 		    SECTOR_SIZE - STAGE1_SIG);
1874*d7802caeSToomas Soome 	}
1875*d7802caeSToomas Soome 
1876*d7802caeSToomas Soome 	bcopy(uuid, dest + STAGE1_STAGE2_UUID, UUID_LEN);
1877*d7802caeSToomas Soome 
1878*d7802caeSToomas Soome 	/* set stage2 size */
1879*d7802caeSToomas Soome 	bblk = stage2->pl_src_data;
1880*d7802caeSToomas Soome 	size = bblk->buf_size / SECTOR_SIZE;
1881*d7802caeSToomas Soome 	*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = size;
1882*d7802caeSToomas Soome 
1883*d7802caeSToomas Soome 	/* set stage2 LBA */
1884*d7802caeSToomas Soome 	*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
1885*d7802caeSToomas Soome 	    device->stage.start + device->stage.offset;
1886*d7802caeSToomas Soome 
1887*d7802caeSToomas Soome 	/* Copy prepared data to stage1 block read from the disk. */
1888*d7802caeSToomas Soome 	bcopy(dest, src, SECTOR_SIZE);
1889*d7802caeSToomas Soome }
1890*d7802caeSToomas Soome 
1891*d7802caeSToomas Soome static void
1892*d7802caeSToomas Soome prepare_bootblock(ib_data_t *data, struct partlist *pl, char *updt_str)
1893*d7802caeSToomas Soome {
1894*d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
1895*d7802caeSToomas Soome 	uint64_t		*ptr;
1896*d7802caeSToomas Soome 
1897*d7802caeSToomas Soome 	assert(pl != NULL);
1898*d7802caeSToomas Soome 
1899*d7802caeSToomas Soome 	bblock = pl->pl_src_data;
1900*d7802caeSToomas Soome 
1901*d7802caeSToomas Soome 	ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
1902*d7802caeSToomas Soome 	*ptr = data->target.start;
1903*d7802caeSToomas Soome 
1904*d7802caeSToomas Soome 	/*
1905*d7802caeSToomas Soome 	 * the loader bootblock has built in version, if custom
1906*d7802caeSToomas Soome 	 * version was provided, update it.
1907*d7802caeSToomas Soome 	 */
1908*d7802caeSToomas Soome 	if (do_version)
1909*d7802caeSToomas Soome 		add_bootblock_einfo(bblock, updt_str);
19100c946d80SToomas Soome }
19110c946d80SToomas Soome 
19120c946d80SToomas Soome static int
1913*d7802caeSToomas Soome open_device(const char *path)
19140c946d80SToomas Soome {
19150c946d80SToomas Soome 	struct stat	statbuf = {0};
19160c946d80SToomas Soome 	int		fd = -1;
19170c946d80SToomas Soome 
19180c946d80SToomas Soome 	if (nowrite)
19190c946d80SToomas Soome 		fd = open(path, O_RDONLY);
19200c946d80SToomas Soome 	else
19210c946d80SToomas Soome 		fd = open(path, O_RDWR);
19220c946d80SToomas Soome 
19230c946d80SToomas Soome 	if (fd == -1) {
19240c946d80SToomas Soome 		BOOT_DEBUG("Unable to open %s\n", path);
19250c946d80SToomas Soome 		perror("open");
19260c946d80SToomas Soome 		return (-1);
19270c946d80SToomas Soome 	}
19280c946d80SToomas Soome 
19290c946d80SToomas Soome 	if (fstat(fd, &statbuf) != 0) {
19300c946d80SToomas Soome 		BOOT_DEBUG("Unable to stat %s\n", path);
19310c946d80SToomas Soome 		perror("stat");
19320c946d80SToomas Soome 		(void) close(fd);
19330c946d80SToomas Soome 		return (-1);
19340c946d80SToomas Soome 	}
19350c946d80SToomas Soome 
19360c946d80SToomas Soome 	if (S_ISCHR(statbuf.st_mode) == 0) {
19370c946d80SToomas Soome 		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
19380c946d80SToomas Soome 		    path);
19390c946d80SToomas Soome 		(void) close(fd);
19400c946d80SToomas Soome 		return (-1);
19410c946d80SToomas Soome 	}
19420c946d80SToomas Soome 
19430c946d80SToomas Soome 	return (fd);
19440c946d80SToomas Soome }
19450c946d80SToomas Soome 
1946*d7802caeSToomas Soome /*
1947*d7802caeSToomas Soome  * We need to record stage2 location and size into pmbr/vbr.
1948*d7802caeSToomas Soome  * We need to record target partiton LBA to stage2.
1949*d7802caeSToomas Soome  */
1950*d7802caeSToomas Soome static void
1951*d7802caeSToomas Soome prepare_bblocks(ib_data_t *data)
19520c946d80SToomas Soome {
1953*d7802caeSToomas Soome 	struct partlist *pl;
1954*d7802caeSToomas Soome 	struct partlist *mbr, *stage1, *stage2;
1955*d7802caeSToomas Soome 	uuid_t uuid;
19560c946d80SToomas Soome 
1957*d7802caeSToomas Soome 	mbr = stage1 = stage2 = NULL;
1958*d7802caeSToomas Soome 	/*
1959*d7802caeSToomas Soome 	 * Walk list and pick up BIOS boot blocks. EFI boot programs
1960*d7802caeSToomas Soome 	 * can be set in place.
1961*d7802caeSToomas Soome 	 */
1962*d7802caeSToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
1963*d7802caeSToomas Soome 		switch (pl->pl_type) {
1964*d7802caeSToomas Soome 		case IB_BBLK_MBR:
1965*d7802caeSToomas Soome 			mbr = pl;
1966*d7802caeSToomas Soome 			break;
1967*d7802caeSToomas Soome 		case IB_BBLK_STAGE1:
1968*d7802caeSToomas Soome 			stage1 = pl;
1969*d7802caeSToomas Soome 			break;
1970*d7802caeSToomas Soome 		case IB_BBLK_STAGE2:
1971*d7802caeSToomas Soome 			stage2 = pl;
1972*d7802caeSToomas Soome 			/* FALLTHROUGH */
1973*d7802caeSToomas Soome 		case IB_BBLK_EFI:
1974*d7802caeSToomas Soome 			prepare_bootblock(data, pl, update_str);
1975*d7802caeSToomas Soome 			break;
1976*d7802caeSToomas Soome 		default:
19770c946d80SToomas Soome 			break;
19780c946d80SToomas Soome 		}
19790c946d80SToomas Soome 	}
19800c946d80SToomas Soome 
1981*d7802caeSToomas Soome 	/* If stage2 is missing, we are done. */
1982*d7802caeSToomas Soome 	if (stage2 == NULL)
1983*d7802caeSToomas Soome 		return;
19840c946d80SToomas Soome 
1985*d7802caeSToomas Soome 	/*
1986*d7802caeSToomas Soome 	 * Create disk uuid. We only need reasonable amount of uniqueness
1987*d7802caeSToomas Soome 	 * to allow biosdev to identify disk based on mbr differences.
1988*d7802caeSToomas Soome 	 */
1989*d7802caeSToomas Soome 	uuid_generate(uuid);
19900c946d80SToomas Soome 
1991*d7802caeSToomas Soome 	if (mbr != NULL) {
1992*d7802caeSToomas Soome 		prepare_stage1(mbr, stage2, uuid);
19930c946d80SToomas Soome 
19940c946d80SToomas Soome 		/*
1995*d7802caeSToomas Soome 		 * If we have stage1, we point MBR to read stage 1.
19960c946d80SToomas Soome 		 */
1997*d7802caeSToomas Soome 		if (stage1 != NULL) {
1998*d7802caeSToomas Soome 			char *dest = mbr->pl_stage;
19990c946d80SToomas Soome 
2000*d7802caeSToomas Soome 			*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = 1;
2001*d7802caeSToomas Soome 			*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
2002*d7802caeSToomas Soome 			    stage1->pl_device->stage.start;
20030c946d80SToomas Soome 		}
20040c946d80SToomas Soome 	}
20050c946d80SToomas Soome 
2006*d7802caeSToomas Soome 	if (stage1 != NULL) {
2007*d7802caeSToomas Soome 		prepare_stage1(stage1, stage2, uuid);
20080c946d80SToomas Soome 	}
20090c946d80SToomas Soome }
20100c946d80SToomas Soome 
20110c946d80SToomas Soome /*
20120c946d80SToomas Soome  * Install a new bootblock on the given device. handle_install() expects argv
20130c946d80SToomas Soome  * to contain 3 parameters (the target device path and the path to the
20140c946d80SToomas Soome  * bootblock.
20150c946d80SToomas Soome  *
20160c946d80SToomas Soome  * Returns:	BC_SUCCESS - if the installation is successful
20170c946d80SToomas Soome  *		BC_ERROR   - if the installation failed
20180c946d80SToomas Soome  *		BC_NOUPDT  - if no installation was performed because the
20190c946d80SToomas Soome  *		             version currently installed is more recent than the
20200c946d80SToomas Soome  *			     supplied one.
20210c946d80SToomas Soome  *
20220c946d80SToomas Soome  */
20230c946d80SToomas Soome static int
2024*d7802caeSToomas Soome handle_install(char *progname, int argc, char **argv)
20250c946d80SToomas Soome {
2026*d7802caeSToomas Soome 	struct partlist	*pl;
2027*d7802caeSToomas Soome 	ib_data_t	data = { 0 };
20280c946d80SToomas Soome 	char		*device_path = NULL;
20290c946d80SToomas Soome 	int		ret = BC_ERROR;
20300c946d80SToomas Soome 
2031*d7802caeSToomas Soome 	switch (argc) {
2032*d7802caeSToomas Soome 	case 1:
2033*d7802caeSToomas Soome 		if ((device_path = strdup(argv[0])) == NULL) {
2034*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2035*d7802caeSToomas Soome 			goto done;
2036*d7802caeSToomas Soome 		}
2037*d7802caeSToomas Soome 		if (asprintf(&stage1, "%s/%s", boot_dir, STAGE1) < 0) {
2038*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2039*d7802caeSToomas Soome 			goto done;
2040*d7802caeSToomas Soome 		}
2041*d7802caeSToomas Soome 		if (asprintf(&stage2, "%s/%s", boot_dir, STAGE2) < 0) {
2042*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2043*d7802caeSToomas Soome 			goto done;
2044*d7802caeSToomas Soome 		}
2045*d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2046*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2047*d7802caeSToomas Soome 			goto done;
2048*d7802caeSToomas Soome 		}
2049*d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2050*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2051*d7802caeSToomas Soome 			goto done;
2052*d7802caeSToomas Soome 		}
2053*d7802caeSToomas Soome 		break;
2054*d7802caeSToomas Soome 	case 3:
2055*d7802caeSToomas Soome 		if ((stage1 = strdup(argv[0])) == NULL) {
2056*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2057*d7802caeSToomas Soome 			goto done;
2058*d7802caeSToomas Soome 		}
2059*d7802caeSToomas Soome 		if ((stage2 = strdup(argv[1])) == NULL) {
2060*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2061*d7802caeSToomas Soome 			goto done;
2062*d7802caeSToomas Soome 		}
2063*d7802caeSToomas Soome 		if ((device_path = strdup(argv[2])) == NULL) {
2064*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2065*d7802caeSToomas Soome 			goto done;
2066*d7802caeSToomas Soome 		}
2067*d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2068*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2069*d7802caeSToomas Soome 			goto done;
2070*d7802caeSToomas Soome 		}
2071*d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2072*d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2073*d7802caeSToomas Soome 			goto done;
2074*d7802caeSToomas Soome 		}
2075*d7802caeSToomas Soome 		break;
2076*d7802caeSToomas Soome 	default:
2077*d7802caeSToomas Soome 		usage(progname, ret);
2078*d7802caeSToomas Soome 	}
20790c946d80SToomas Soome 
2080*d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2081*d7802caeSToomas Soome 	if (data.plist == NULL) {
2082*d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2083*d7802caeSToomas Soome 		goto done;
20840c946d80SToomas Soome 	}
2085*d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
20860c946d80SToomas Soome 
20870c946d80SToomas Soome 	BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
2088*d7802caeSToomas Soome 	    device_path, stage1, stage2);
20890c946d80SToomas Soome 
2090*d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2091*d7802caeSToomas Soome 		/* Read all data. */
2092*d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2093*d7802caeSToomas Soome 			if (!pl->pl_cb.read(pl)) {
2094*d7802caeSToomas Soome 				printf("\n");
2095*d7802caeSToomas Soome 			}
2096*d7802caeSToomas Soome 			if (!pl->pl_cb.read_bbl(pl)) {
2097*d7802caeSToomas Soome 				(void) fprintf(stderr,
2098*d7802caeSToomas Soome 				    gettext("Error reading %s\n"),
2099*d7802caeSToomas Soome 				    pl->pl_src_name);
2100*d7802caeSToomas Soome 				goto cleanup;
2101*d7802caeSToomas Soome 			}
2102*d7802caeSToomas Soome 		}
21030c946d80SToomas Soome 
2104*d7802caeSToomas Soome 		/* Prepare data. */
2105*d7802caeSToomas Soome 		prepare_bblocks(&data);
2106*d7802caeSToomas Soome 
2107*d7802caeSToomas Soome 		/* Commit data to disk. */
2108*d7802caeSToomas Soome 		while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) !=
2109*d7802caeSToomas Soome 		    NULL) {
2110*d7802caeSToomas Soome 			if (pl->pl_cb.compare != NULL &&
2111*d7802caeSToomas Soome 			    pl->pl_cb.compare(pl)) {
2112*d7802caeSToomas Soome 				if (pl->pl_cb.install != NULL)
2113*d7802caeSToomas Soome 					pl->pl_cb.install(&data, pl);
2114*d7802caeSToomas Soome 			} else {
2115*d7802caeSToomas Soome 				printf("\n");
2116*d7802caeSToomas Soome 			}
2117*d7802caeSToomas Soome 			STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2118*d7802caeSToomas Soome 			partlist_free(pl);
2119*d7802caeSToomas Soome 		}
21200c946d80SToomas Soome 	}
2121*d7802caeSToomas Soome 	ret = BC_SUCCESS;
21220c946d80SToomas Soome 
2123*d7802caeSToomas Soome cleanup:
2124*d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2125*d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2126*d7802caeSToomas Soome 		partlist_free(pl);
21270c946d80SToomas Soome 	}
2128*d7802caeSToomas Soome 	free(data.plist);
2129*d7802caeSToomas Soome done:
21300c946d80SToomas Soome 	free(stage1);
2131*d7802caeSToomas Soome 	free(stage2);
2132*d7802caeSToomas Soome 	free(efi32);
2133*d7802caeSToomas Soome 	free(efi64);
21340c946d80SToomas Soome 	free(device_path);
21350c946d80SToomas Soome 	return (ret);
21360c946d80SToomas Soome }
21370c946d80SToomas Soome 
21380c946d80SToomas Soome /*
21390c946d80SToomas Soome  * Retrieves from a device the extended information (einfo) associated to the
2140b6dfa2aeSToomas Soome  * file or installed stage2.
2141b6dfa2aeSToomas Soome  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
2142b6dfa2aeSToomas Soome  * or file name.
21430c946d80SToomas Soome  * Returns:
21440c946d80SToomas Soome  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
21450c946d80SToomas Soome  *	  - BC_ERROR (on error)
21460c946d80SToomas Soome  *        - BC_NOEINFO (no extended information available)
21470c946d80SToomas Soome  */
21480c946d80SToomas Soome static int
2149*d7802caeSToomas Soome handle_getinfo(char *progname, int argc, char **argv)
21500c946d80SToomas Soome {
2151*d7802caeSToomas Soome 	struct partlist	*pl;
2152*d7802caeSToomas Soome 	ib_data_t	data = { 0 };
2153*d7802caeSToomas Soome 	char		*device_path;
21540c946d80SToomas Soome 
2155*d7802caeSToomas Soome 	if (argc != 1) {
21560c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2157bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
21580c946d80SToomas Soome 	}
21590c946d80SToomas Soome 
2160*d7802caeSToomas Soome 	if ((device_path = strdup(argv[0])) == NULL) {
2161*d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2162*d7802caeSToomas Soome 		return (BC_ERROR);
21630c946d80SToomas Soome 	}
21640c946d80SToomas Soome 
2165*d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2166*d7802caeSToomas Soome 	if (data.plist == NULL) {
2167*d7802caeSToomas Soome 		perror("malloc");
2168*d7802caeSToomas Soome 		free(device_path);
2169*d7802caeSToomas Soome 		return (BC_ERROR);
21700c946d80SToomas Soome 	}
2171*d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
21720c946d80SToomas Soome 
2173*d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2174*d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2175*d7802caeSToomas Soome 			if (pl->pl_cb.read(pl))
2176*d7802caeSToomas Soome 				pl->pl_cb.print(pl);
2177*d7802caeSToomas Soome 			else
2178*d7802caeSToomas Soome 				printf("\n");
2179*d7802caeSToomas Soome 		}
21800c946d80SToomas Soome 	}
21810c946d80SToomas Soome 
2182*d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2183*d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2184*d7802caeSToomas Soome 		partlist_free(pl);
21850c946d80SToomas Soome 	}
2186*d7802caeSToomas Soome 	free(data.plist);
21870c946d80SToomas Soome 
2188*d7802caeSToomas Soome 	return (BC_SUCCESS);
21890c946d80SToomas Soome }
21900c946d80SToomas Soome 
21910c946d80SToomas Soome /*
21920c946d80SToomas Soome  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
21930c946d80SToomas Soome  *
21940c946d80SToomas Soome  * Returns:
21950c946d80SToomas Soome  *	- BC_SUCCESS (a successful propagation happened)
21960c946d80SToomas Soome  *	- BC_ERROR (an error occurred)
21970c946d80SToomas Soome  *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
21980c946d80SToomas Soome  *			there is no multiboot information)
21990c946d80SToomas Soome  */
22000c946d80SToomas Soome static int
2201*d7802caeSToomas Soome handle_mirror(char *progname, int argc, char **argv)
22020c946d80SToomas Soome {
2203*d7802caeSToomas Soome 	ib_data_t src = { 0 };
2204*d7802caeSToomas Soome 	ib_data_t dest = { 0 };
2205*d7802caeSToomas Soome 	struct partlist *pl_src, *pl_dest;
2206*d7802caeSToomas Soome 	char		*curr_device_path = NULL;
2207*d7802caeSToomas Soome 	char		*attach_device_path = NULL;
22080c946d80SToomas Soome 	int		retval = BC_ERROR;
22090c946d80SToomas Soome 
2210*d7802caeSToomas Soome 	if (argc == 2) {
2211*d7802caeSToomas Soome 		curr_device_path = strdup(argv[0]);
2212*d7802caeSToomas Soome 		attach_device_path = strdup(argv[1]);
2213*d7802caeSToomas Soome 	}
22140c946d80SToomas Soome 
22150c946d80SToomas Soome 	if (!curr_device_path || !attach_device_path) {
2216*d7802caeSToomas Soome 		free(curr_device_path);
2217*d7802caeSToomas Soome 		free(attach_device_path);
22180c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2219bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22200c946d80SToomas Soome 	}
22210c946d80SToomas Soome 	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
22220c946d80SToomas Soome 	    " %s\n", curr_device_path, attach_device_path);
22230c946d80SToomas Soome 
2224*d7802caeSToomas Soome 	src.plist = malloc(sizeof (*src.plist));
2225*d7802caeSToomas Soome 	if (src.plist == NULL) {
2226*d7802caeSToomas Soome 		perror("malloc");
2227*d7802caeSToomas Soome 		return (BC_ERROR);
2228*d7802caeSToomas Soome 	}
2229*d7802caeSToomas Soome 	STAILQ_INIT(src.plist);
2230*d7802caeSToomas Soome 
2231*d7802caeSToomas Soome 	dest.plist = malloc(sizeof (*dest.plist));
2232*d7802caeSToomas Soome 	if (dest.plist == NULL) {
2233*d7802caeSToomas Soome 		perror("malloc");
2234*d7802caeSToomas Soome 		goto out;
2235*d7802caeSToomas Soome 	}
2236*d7802caeSToomas Soome 	STAILQ_INIT(dest.plist);
22370c946d80SToomas Soome 
2238*d7802caeSToomas Soome 	if (!probe_device(&src, curr_device_path)) {
22390c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22400c946d80SToomas Soome 		    "information from %s (current device)\n"),
22410c946d80SToomas Soome 		    curr_device_path);
2242*d7802caeSToomas Soome 		goto out;
22430c946d80SToomas Soome 	}
22440c946d80SToomas Soome 
2245*d7802caeSToomas Soome 	if (!probe_device(&dest, attach_device_path) != BC_SUCCESS) {
22460c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22470c946d80SToomas Soome 		    "information from %s (attaching device)\n"),
22480c946d80SToomas Soome 		    attach_device_path);
2249*d7802caeSToomas Soome 		goto cleanup_src;
22500c946d80SToomas Soome 	}
22510c946d80SToomas Soome 
2252*d7802caeSToomas Soome 	write_mbr = true;
2253*d7802caeSToomas Soome 	force_mbr = true;
2254*d7802caeSToomas Soome 
2255*d7802caeSToomas Soome 	pl_dest = STAILQ_FIRST(dest.plist);
2256*d7802caeSToomas Soome 	STAILQ_FOREACH(pl_src, src.plist, pl_next) {
2257*d7802caeSToomas Soome 		if (pl_dest == NULL) {
2258*d7802caeSToomas Soome 			(void) fprintf(stderr,
2259*d7802caeSToomas Soome 			    gettext("Destination disk layout is different "
2260*d7802caeSToomas Soome 			    "from source, can not mirror.\n"));
2261*d7802caeSToomas Soome 			goto cleanup;
2262*d7802caeSToomas Soome 		}
2263*d7802caeSToomas Soome 		if (!pl_src->pl_cb.read(pl_src)) {
2264*d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2265*d7802caeSToomas Soome 			    "boot block from %s\n"), pl_src->pl_devname);
2266*d7802caeSToomas Soome 			goto cleanup;
2267*d7802caeSToomas Soome 		}
2268*d7802caeSToomas Soome 		if (!pl_dest->pl_cb.read(pl_dest)) {
2269*d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2270*d7802caeSToomas Soome 			    "boot block from %s\n"), pl_dest->pl_devname);
2271*d7802caeSToomas Soome 		}
2272*d7802caeSToomas Soome 
2273*d7802caeSToomas Soome 		/* Set source pl_stage to destination source data */
2274*d7802caeSToomas Soome 		pl_dest->pl_src_data = pl_src->pl_stage;
2275*d7802caeSToomas Soome 		pl_src->pl_stage = NULL;
22760c946d80SToomas Soome 
2277*d7802caeSToomas Soome 		pl_dest = STAILQ_NEXT(pl_dest, pl_next);
22780c946d80SToomas Soome 	}
22790c946d80SToomas Soome 
2280*d7802caeSToomas Soome 	/* Prepare data. */
2281*d7802caeSToomas Soome 	prepare_bblocks(&dest);
22820c946d80SToomas Soome 
2283*d7802caeSToomas Soome 	/* Commit data to disk. */
2284*d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2285*d7802caeSToomas Soome 		pl_dest->pl_cb.install(&dest, pl_dest);
2286*d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2287*d7802caeSToomas Soome 		partlist_free(pl_dest);
2288*d7802caeSToomas Soome 
2289*d7802caeSToomas Soome 		/* Free source list */
2290*d7802caeSToomas Soome 		pl_src = STAILQ_LAST(src.plist, partlist, pl_next);
2291*d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2292*d7802caeSToomas Soome 		partlist_free(pl_src);
2293*d7802caeSToomas Soome 	}
2294*d7802caeSToomas Soome 	retval = BC_SUCCESS;
2295*d7802caeSToomas Soome 
2296*d7802caeSToomas Soome cleanup:
2297*d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2298*d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2299*d7802caeSToomas Soome 		partlist_free(pl_dest);
2300*d7802caeSToomas Soome 	}
2301*d7802caeSToomas Soome 	free(dest.plist);
2302*d7802caeSToomas Soome cleanup_src:
2303*d7802caeSToomas Soome 	while ((pl_src = STAILQ_LAST(src.plist, partlist, pl_next)) != NULL) {
2304*d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2305*d7802caeSToomas Soome 		partlist_free(pl_src);
2306*d7802caeSToomas Soome 	}
2307*d7802caeSToomas Soome 	free(src.plist);
2308*d7802caeSToomas Soome out:
23090c946d80SToomas Soome 	free(curr_device_path);
23100c946d80SToomas Soome 	free(attach_device_path);
23110c946d80SToomas Soome 	return (retval);
23120c946d80SToomas Soome }
23130c946d80SToomas Soome 
2314*d7802caeSToomas Soome #define	USAGE_STRING	\
2315*d7802caeSToomas Soome "Usage:\t%s [-fFmn] [-b boot_dir] [-u verstr]\n"	\
2316*d7802caeSToomas Soome "\t\t[stage1 stage2] raw-device\n"			\
2317*d7802caeSToomas Soome "\t%s -M [-n] raw-device attach-raw-device\n"		\
2318*d7802caeSToomas Soome "\t%s [-e|-V] -i raw-device | file\n"
23190c946d80SToomas Soome 
23200c946d80SToomas Soome #define	CANON_USAGE_STR	gettext(USAGE_STRING)
23210c946d80SToomas Soome 
23220c946d80SToomas Soome static void
2323bdecfb1eSToomas Soome usage(char *progname, int rc)
23240c946d80SToomas Soome {
23250c946d80SToomas Soome 	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
2326*d7802caeSToomas Soome 	fini_yes();
2327bdecfb1eSToomas Soome 	exit(rc);
23280c946d80SToomas Soome }
23290c946d80SToomas Soome 
23300c946d80SToomas Soome int
23310c946d80SToomas Soome main(int argc, char **argv)
23320c946d80SToomas Soome {
23330c946d80SToomas Soome 	int	opt;
23340c946d80SToomas Soome 	int	ret;
23350c946d80SToomas Soome 	char	*progname;
2336*d7802caeSToomas Soome 	struct stat sb;
23370c946d80SToomas Soome 
23380c946d80SToomas Soome 	(void) setlocale(LC_ALL, "");
23390c946d80SToomas Soome 	(void) textdomain(TEXT_DOMAIN);
2340bdecfb1eSToomas Soome 	if (init_yes() < 0)
2341bdecfb1eSToomas Soome 		errx(BC_ERROR, gettext(ERR_MSG_INIT_YES), strerror(errno));
2342bdecfb1eSToomas Soome 
2343*d7802caeSToomas Soome 	/* Needed for mount pcfs. */
2344*d7802caeSToomas Soome 	tzset();
2345*d7802caeSToomas Soome 
2346bdecfb1eSToomas Soome 	/* Determine our name */
2347bdecfb1eSToomas Soome 	progname = basename(argv[0]);
23480c946d80SToomas Soome 
2349*d7802caeSToomas Soome 	while ((opt = getopt(argc, argv, "b:deFfhiMmnu:V")) != EOF) {
23500c946d80SToomas Soome 		switch (opt) {
2351*d7802caeSToomas Soome 		case 'b':
2352*d7802caeSToomas Soome 			boot_dir = strdup(optarg);
2353*d7802caeSToomas Soome 			if (boot_dir == NULL) {
2354*d7802caeSToomas Soome 				err(BC_ERROR,
2355*d7802caeSToomas Soome 				    gettext("Memory allocation failure"));
2356*d7802caeSToomas Soome 			}
2357*d7802caeSToomas Soome 			if (lstat(boot_dir, &sb) != 0) {
2358*d7802caeSToomas Soome 				err(BC_ERROR, boot_dir);
2359*d7802caeSToomas Soome 			}
2360*d7802caeSToomas Soome 			if (!S_ISDIR(sb.st_mode)) {
2361*d7802caeSToomas Soome 				errx(BC_ERROR, gettext("%s: not a directory"),
2362*d7802caeSToomas Soome 				    boot_dir);
2363*d7802caeSToomas Soome 			}
2364*d7802caeSToomas Soome 			break;
23650c946d80SToomas Soome 		case 'd':
2366*d7802caeSToomas Soome 			boot_debug = true;
23670c946d80SToomas Soome 			break;
23680c946d80SToomas Soome 		case 'e':
2369*d7802caeSToomas Soome 			strip = true;
23700c946d80SToomas Soome 			break;
23710c946d80SToomas Soome 		case 'F':
2372*d7802caeSToomas Soome 			force_update = true;
23730c946d80SToomas Soome 			break;
23740c946d80SToomas Soome 		case 'f':
2375*d7802caeSToomas Soome 			force_mbr = true;
23760c946d80SToomas Soome 			break;
23770c946d80SToomas Soome 		case 'h':
2378bdecfb1eSToomas Soome 			usage(progname, BC_SUCCESS);
23790c946d80SToomas Soome 			break;
23800c946d80SToomas Soome 		case 'i':
2381*d7802caeSToomas Soome 			do_getinfo = true;
23820c946d80SToomas Soome 			break;
23830c946d80SToomas Soome 		case 'M':
2384*d7802caeSToomas Soome 			do_mirror_bblk = true;
23850c946d80SToomas Soome 			break;
23860c946d80SToomas Soome 		case 'm':
2387*d7802caeSToomas Soome 			write_mbr = true;
23880c946d80SToomas Soome 			break;
23890c946d80SToomas Soome 		case 'n':
2390*d7802caeSToomas Soome 			nowrite = true;
23910c946d80SToomas Soome 			break;
23920c946d80SToomas Soome 		case 'u':
2393*d7802caeSToomas Soome 			do_version = true;
23940c946d80SToomas Soome 
2395bdecfb1eSToomas Soome 			update_str = strdup(optarg);
23960c946d80SToomas Soome 			if (update_str == NULL) {
23970c946d80SToomas Soome 				perror(gettext("Memory allocation failure"));
23980c946d80SToomas Soome 				exit(BC_ERROR);
23990c946d80SToomas Soome 			}
24000c946d80SToomas Soome 			break;
24010c946d80SToomas Soome 		case 'V':
2402*d7802caeSToomas Soome 			verbose_dump = true;
24030c946d80SToomas Soome 			break;
24040c946d80SToomas Soome 		default:
24050c946d80SToomas Soome 			/* fall through to process non-optional args */
24060c946d80SToomas Soome 			break;
24070c946d80SToomas Soome 		}
24080c946d80SToomas Soome 	}
24090c946d80SToomas Soome 
24100c946d80SToomas Soome 	/* check arguments */
24110c946d80SToomas Soome 	check_options(progname);
24120c946d80SToomas Soome 
24130c946d80SToomas Soome 	if (nowrite)
24140c946d80SToomas Soome 		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
24150c946d80SToomas Soome 		    " be written to disk.\n"));
24160c946d80SToomas Soome 
24170c946d80SToomas Soome 	if (do_getinfo) {
2418*d7802caeSToomas Soome 		ret = handle_getinfo(progname, argc - optind, argv + optind);
24190c946d80SToomas Soome 	} else if (do_mirror_bblk) {
2420*d7802caeSToomas Soome 		ret = handle_mirror(progname, argc - optind, argv + optind);
24210c946d80SToomas Soome 	} else {
2422*d7802caeSToomas Soome 		ret = handle_install(progname, argc - optind, argv + optind);
24230c946d80SToomas Soome 	}
2424*d7802caeSToomas Soome 	fini_yes();
24250c946d80SToomas Soome 	return (ret);
24260c946d80SToomas Soome }
24270c946d80SToomas Soome 
24280c946d80SToomas Soome #define	MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
24290c946d80SToomas Soome static void
24300c946d80SToomas Soome check_options(char *progname)
24310c946d80SToomas Soome {
24320c946d80SToomas Soome 	if (do_getinfo && do_mirror_bblk) {
24330c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
24340c946d80SToomas Soome 		    "specified at the same time\n"));
2435bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
24360c946d80SToomas Soome 	}
24370c946d80SToomas Soome 
24380c946d80SToomas Soome 	if (do_mirror_bblk) {
24390c946d80SToomas Soome 		/*
24400c946d80SToomas Soome 		 * -u and -F may actually reflect a user intent that is not
24410c946d80SToomas Soome 		 * correct with this command (mirror can be interpreted
24420c946d80SToomas Soome 		 * "similar" to install. Emit a message and continue.
24430c946d80SToomas Soome 		 * -e and -V have no meaning, be quiet here and only report the
24440c946d80SToomas Soome 		 * incongruence if a debug output is requested.
24450c946d80SToomas Soome 		 */
24460c946d80SToomas Soome 		if (do_version) {
24470c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-u");
2448*d7802caeSToomas Soome 			do_version = false;
24490c946d80SToomas Soome 		}
24500c946d80SToomas Soome 		if (force_update) {
24510c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-F");
2452*d7802caeSToomas Soome 			force_update = false;
24530c946d80SToomas Soome 		}
24540c946d80SToomas Soome 		if (strip || verbose_dump) {
24550c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
2456*d7802caeSToomas Soome 			strip = false;
2457*d7802caeSToomas Soome 			verbose_dump = false;
24580c946d80SToomas Soome 		}
24590c946d80SToomas Soome 	}
24600c946d80SToomas Soome 
2461*d7802caeSToomas Soome 	if ((strip || verbose_dump) && !do_getinfo)
2462*d7802caeSToomas Soome 		usage(progname, BC_ERROR);
2463*d7802caeSToomas Soome 
24640c946d80SToomas Soome 	if (do_getinfo) {
24650c946d80SToomas Soome 		if (write_mbr || force_mbr || do_version || force_update) {
24660c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
2467*d7802caeSToomas Soome 			write_mbr = force_mbr = do_version = false;
2468*d7802caeSToomas Soome 			force_update = false;
24690c946d80SToomas Soome 		}
24700c946d80SToomas Soome 	}
24710c946d80SToomas Soome }
2472