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.
24d7802caeSToomas Soome  * Copyright 2019 Toomas Soome <tsoome@me.com>
250c946d80SToomas Soome  */
260c946d80SToomas Soome 
270c946d80SToomas Soome #include <stdio.h>
28d7802caeSToomas 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>
37d7802caeSToomas Soome #include <time.h>
38d7802caeSToomas 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>
48d7802caeSToomas Soome #include <sys/queue.h>
49d7802caeSToomas Soome #include <sys/mount.h>
50d7802caeSToomas Soome #include <sys/mntent.h>
51d7802caeSToomas Soome #include <sys/mnttab.h>
52d7802caeSToomas 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 
108d7802caeSToomas Soome static bool	write_mbr = false;
109d7802caeSToomas Soome static bool	force_mbr = false;
110d7802caeSToomas Soome static bool	force_update = false;
111d7802caeSToomas Soome static bool	do_getinfo = false;
112d7802caeSToomas Soome static bool	do_version = false;
113d7802caeSToomas Soome static bool	do_mirror_bblk = false;
114d7802caeSToomas Soome static bool	strip = false;
115d7802caeSToomas Soome static bool	verbose_dump = false;
116d7802caeSToomas Soome static size_t	sector_size = SECTOR_SIZE;
1170c946d80SToomas Soome 
1180c946d80SToomas Soome /* Versioning string, if present. */
1190c946d80SToomas Soome static char		*update_str;
1200c946d80SToomas Soome 
121d7802caeSToomas Soome /* Default location of boot programs. */
122d7802caeSToomas Soome static char		*boot_dir = "/boot";
123d7802caeSToomas Soome 
124d7802caeSToomas Soome /* Our boot programs */
125d7802caeSToomas Soome #define	STAGE1		"pmbr"
126d7802caeSToomas Soome #define	STAGE2		"gptzfsboot"
127d7802caeSToomas Soome #define	BOOTIA32	"bootia32.efi"
128d7802caeSToomas Soome #define	BOOTX64		"bootx64.efi"
129d7802caeSToomas Soome #define	LOADER32	"loader32.efi"
130d7802caeSToomas Soome #define	LOADER64	"loader64.efi"
131d7802caeSToomas Soome 
132d7802caeSToomas Soome static char *stage1;
133d7802caeSToomas Soome static char *stage2;
134d7802caeSToomas Soome static char *efi32;
135d7802caeSToomas Soome static char *efi64;
136d7802caeSToomas Soome 
137d7802caeSToomas Soome #define	GRUB_VERSION_OFF (0x3e)
138d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MAJOR 3
139d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MINOR 2
140d7802caeSToomas Soome #define	GRUB_VERSION (2 << 8 | 3) /* 3.2 */
141d7802caeSToomas Soome 
142d7802caeSToomas Soome #define	LOADER_VERSION (1)
143d7802caeSToomas Soome #define	LOADER_JOYENT_VERSION (2)
144d7802caeSToomas Soome 
145d7802caeSToomas Soome typedef enum {
146d7802caeSToomas Soome 	MBR_TYPE_UNKNOWN,
147d7802caeSToomas Soome 	MBR_TYPE_GRUB1,
148d7802caeSToomas Soome 	MBR_TYPE_LOADER,
149d7802caeSToomas Soome 	MBR_TYPE_LOADER_JOYENT,
150d7802caeSToomas Soome } mbr_type_t;
151d7802caeSToomas 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 *);
160d7802caeSToomas Soome static int open_device(const char *);
161d7802caeSToomas Soome static char *make_blkdev(const char *);
1620c946d80SToomas Soome 
163d7802caeSToomas Soome static int read_bootblock_from_file(const char *, ib_bootblock_t *);
1640c946d80SToomas Soome static void add_bootblock_einfo(ib_bootblock_t *, char *);
165d7802caeSToomas Soome static void prepare_bootblock(ib_data_t *, struct partlist *, char *);
166d7802caeSToomas Soome static int handle_install(char *, int, char **);
167d7802caeSToomas Soome static int handle_getinfo(char *, int, char **);
168d7802caeSToomas Soome static int handle_mirror(char *, int, char **);
169bdecfb1eSToomas Soome static void usage(char *, int) __NORETURN;
1700c946d80SToomas Soome 
171d7802caeSToomas Soome static char *
172d7802caeSToomas Soome stagefs_mount(char *blkdev, struct partlist *plist)
1730c946d80SToomas Soome {
174d7802caeSToomas Soome 	char *path;
175d7802caeSToomas Soome 	char optbuf[MAX_MNTOPT_STR] = { '\0', };
176d7802caeSToomas Soome 	char *template = strdup("/tmp/ibootXXXXXX");
177d7802caeSToomas Soome 	int ret;
1780c946d80SToomas Soome 
179d7802caeSToomas Soome 	if (template == NULL)
180d7802caeSToomas Soome 		return (NULL);
1810c946d80SToomas Soome 
182d7802caeSToomas Soome 	if ((path = mkdtemp(template)) == NULL) {
183d7802caeSToomas Soome 		free(template);
184d7802caeSToomas Soome 		return (NULL);
1850c946d80SToomas Soome 	}
186d7802caeSToomas Soome 
187d7802caeSToomas Soome 	(void) snprintf(optbuf, MAX_MNTOPT_STR, "timezone=%d",
188d7802caeSToomas Soome 	    timezone);
189d7802caeSToomas Soome 	ret = mount(blkdev, path, MS_OPTIONSTR,
190d7802caeSToomas Soome 	    MNTTYPE_PCFS, NULL, 0, optbuf, MAX_MNTOPT_STR);
191d7802caeSToomas Soome 	if (ret != 0) {
192d7802caeSToomas Soome 		(void) rmdir(path);
193d7802caeSToomas Soome 		free(path);
194d7802caeSToomas Soome 		path = NULL;
195d7802caeSToomas Soome 	}
196d7802caeSToomas Soome 	plist->pl_device->stage.mntpnt = path;
197d7802caeSToomas Soome 	return (path);
1980c946d80SToomas Soome }
1990c946d80SToomas Soome 
200d7802caeSToomas Soome static void
201d7802caeSToomas Soome install_stage1_cb(void *data, struct partlist *plist)
2020c946d80SToomas Soome {
203d7802caeSToomas Soome 	int rv, fd;
204d7802caeSToomas Soome 	ib_device_t *device = plist->pl_device;
2050c946d80SToomas Soome 
206d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR && !write_mbr)
207d7802caeSToomas Soome 		return;
2080c946d80SToomas Soome 
209d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
210d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
211d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
2120c946d80SToomas Soome 		perror("open");
213d7802caeSToomas Soome 		return;
2140c946d80SToomas Soome 	}
2150c946d80SToomas Soome 
216d7802caeSToomas Soome 	rv = write_out(fd, plist->pl_stage, sector_size, 0);
217d7802caeSToomas Soome 	if (rv != BC_SUCCESS) {
218d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot write "
219d7802caeSToomas Soome 		    "partition boot sector\n"));
220d7802caeSToomas Soome 		perror("write");
221d7802caeSToomas Soome 	} else {
222d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("stage1 written to "
223d7802caeSToomas Soome 		    "%s %d sector 0 (abs %d)\n"),
224d7802caeSToomas Soome 		    device->devtype == IB_DEV_MBR? "partition" : "slice",
225d7802caeSToomas Soome 		    device->stage.id, device->stage.start);
2260c946d80SToomas Soome 	}
227d7802caeSToomas Soome }
2280c946d80SToomas Soome 
229d7802caeSToomas Soome static void
230d7802caeSToomas Soome install_stage2_cb(void *data, struct partlist *plist)
231d7802caeSToomas Soome {
232d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
233d7802caeSToomas Soome 	int fd, ret;
234d7802caeSToomas Soome 	off_t offset;
235d7802caeSToomas Soome 	uint64_t abs;
2360c946d80SToomas Soome 
237d7802caeSToomas Soome 	/*
238d7802caeSToomas Soome 	 * ZFS bootblock area is 3.5MB, make sure we can fit.
239d7802caeSToomas Soome 	 * buf_size is size of bootblk+EINFO.
240d7802caeSToomas Soome 	 */
241d7802caeSToomas Soome 	if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
242d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("bootblock is too large\n"));
243d7802caeSToomas Soome 		return;
244d7802caeSToomas Soome 	}
2450c946d80SToomas Soome 
246d7802caeSToomas Soome 	abs = plist->pl_device->stage.start + plist->pl_device->stage.offset;
247d7802caeSToomas Soome 
248d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
249d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
250d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
251d7802caeSToomas Soome 		perror("open");
252d7802caeSToomas Soome 		return;
253d7802caeSToomas Soome 	}
254d7802caeSToomas Soome 	offset = plist->pl_device->stage.offset * SECTOR_SIZE;
255d7802caeSToomas Soome 	ret = write_out(fd, bblock->buf, bblock->buf_size, offset);
256d7802caeSToomas Soome 	(void) close(fd);
257d7802caeSToomas Soome 	if (ret != BC_SUCCESS) {
258d7802caeSToomas Soome 		BOOT_DEBUG("Error writing the ZFS bootblock "
259d7802caeSToomas Soome 		    "to %s at offset %d\n", plist->pl_devname, offset);
260d7802caeSToomas Soome 		return;
261d7802caeSToomas Soome 	}
262d7802caeSToomas Soome 	(void) fprintf(stdout, gettext("bootblock written for %s,"
263d7802caeSToomas Soome 	    " %d sectors starting at %d (abs %lld)\n"), plist->pl_devname,
264d7802caeSToomas Soome 	    (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
265d7802caeSToomas Soome }
266d7802caeSToomas Soome 
267d7802caeSToomas Soome static bool
268d7802caeSToomas Soome mkfs_pcfs(const char *dev)
269d7802caeSToomas Soome {
270d7802caeSToomas Soome 	pid_t pid, w;
271d7802caeSToomas Soome 	posix_spawnattr_t attr;
272d7802caeSToomas Soome 	posix_spawn_file_actions_t file_actions;
273d7802caeSToomas Soome 	int status;
274d7802caeSToomas Soome 	char *cmd[7];
275d7802caeSToomas Soome 
276d7802caeSToomas Soome 	if (posix_spawnattr_init(&attr))
277d7802caeSToomas Soome 		return (false);
278d7802caeSToomas Soome 	if (posix_spawn_file_actions_init(&file_actions)) {
279d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
280d7802caeSToomas Soome 		return (false);
281d7802caeSToomas Soome 	}
282d7802caeSToomas Soome 
283d7802caeSToomas Soome 	if (posix_spawnattr_setflags(&attr,
284d7802caeSToomas Soome 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP)) {
285d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
286d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
287d7802caeSToomas Soome 		return (false);
288d7802caeSToomas Soome 	}
289d7802caeSToomas Soome 	if (posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null",
290d7802caeSToomas Soome 	    O_RDONLY, 0)) {
291d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
292d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
293d7802caeSToomas Soome 		return (false);
294d7802caeSToomas Soome 	}
295d7802caeSToomas Soome 
296d7802caeSToomas Soome 	cmd[0] = "/usr/sbin/mkfs";
297d7802caeSToomas Soome 	cmd[1] = "-F";
298d7802caeSToomas Soome 	cmd[2] = "pcfs";
299d7802caeSToomas Soome 	cmd[3] = "-o";
300d7802caeSToomas Soome 	cmd[4] = "fat=32";
301d7802caeSToomas Soome 	cmd[5] = (char *)dev;
302d7802caeSToomas Soome 	cmd[6] = NULL;
303d7802caeSToomas Soome 
304d7802caeSToomas Soome 	if (posix_spawn(&pid, cmd[0], &file_actions, &attr, cmd, NULL))
305d7802caeSToomas Soome 		return (false);
306d7802caeSToomas Soome 	(void) posix_spawnattr_destroy(&attr);
307d7802caeSToomas Soome 	(void) posix_spawn_file_actions_destroy(&file_actions);
308d7802caeSToomas Soome 
309d7802caeSToomas Soome 	do {
310d7802caeSToomas Soome 		w = waitpid(pid, &status, 0);
311d7802caeSToomas Soome 	} while (w == -1 && errno == EINTR);
312d7802caeSToomas Soome 	if (w == -1)
313d7802caeSToomas Soome 		status = -1;
314d7802caeSToomas Soome 
315d7802caeSToomas Soome 	return (status != -1);
316d7802caeSToomas Soome }
317d7802caeSToomas Soome 
318d7802caeSToomas Soome static void
319d7802caeSToomas Soome install_esp_cb(void *data, struct partlist *plist)
320d7802caeSToomas Soome {
321d7802caeSToomas Soome 	fstyp_handle_t fhdl;
322d7802caeSToomas Soome 	const char *fident;
323d7802caeSToomas Soome 	bool pcfs;
324d7802caeSToomas Soome 	char *blkdev, *path, *file;
325d7802caeSToomas Soome 	FILE *fp;
326d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
327d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
328d7802caeSToomas Soome 	int fd, ret;
329d7802caeSToomas Soome 
330d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
331d7802caeSToomas Soome 		return;
332d7802caeSToomas Soome 
333d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
334d7802caeSToomas Soome 		(void) close(fd);
335d7802caeSToomas Soome 		return;
336d7802caeSToomas Soome 	}
337d7802caeSToomas Soome 
338d7802caeSToomas Soome 	pcfs = false;
339d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) == 0) {
340d7802caeSToomas Soome 		if (strcmp(fident, MNTTYPE_PCFS) == 0)
341d7802caeSToomas Soome 			pcfs = true;
342d7802caeSToomas Soome 	}
343d7802caeSToomas Soome 	fstyp_fini(fhdl);
344d7802caeSToomas Soome 	(void) close(fd);
345d7802caeSToomas Soome 
346d7802caeSToomas Soome 	if (!pcfs) {
347d7802caeSToomas Soome 		(void) printf(gettext("Creating pcfs on ESP %s\n"),
348d7802caeSToomas Soome 		    plist->pl_devname);
349d7802caeSToomas Soome 
350d7802caeSToomas Soome 		if (!mkfs_pcfs(plist->pl_devname)) {
351d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("mkfs -F pcfs failed "
352d7802caeSToomas Soome 			    "on %s\n"), plist->pl_devname);
353d7802caeSToomas Soome 			return;
354d7802caeSToomas Soome 		}
355d7802caeSToomas Soome 	}
356d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
357d7802caeSToomas Soome 	if (blkdev == NULL)
358d7802caeSToomas Soome 		return;
359d7802caeSToomas Soome 
360d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
361d7802caeSToomas Soome 	if (fp == NULL) {
362d7802caeSToomas Soome 		perror("fopen");
363d7802caeSToomas Soome 		free(blkdev);
364d7802caeSToomas Soome 		return;
365d7802caeSToomas Soome 	}
366d7802caeSToomas Soome 
367d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
368d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
369d7802caeSToomas Soome 	(void) fclose(fp);
370d7802caeSToomas Soome 	if (ret == 0)
371d7802caeSToomas Soome 		path = mp.mnt_mountp;
372d7802caeSToomas Soome 	else
373d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
374d7802caeSToomas Soome 
375d7802caeSToomas Soome 	free(blkdev);
376d7802caeSToomas Soome 	if (path == NULL)
377d7802caeSToomas Soome 		return;
378d7802caeSToomas Soome 
379d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, "/EFI") < 0) {
3800c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
381d7802caeSToomas Soome 		return;
3820c946d80SToomas Soome 	}
3830c946d80SToomas Soome 
384d7802caeSToomas Soome 	ret = mkdir(file, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
385d7802caeSToomas Soome 	if (ret == 0 || errno == EEXIST) {
386d7802caeSToomas Soome 		free(file);
387d7802caeSToomas Soome 		if (asprintf(&file, "%s%s", path, "/EFI/Boot") < 0) {
388d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
389d7802caeSToomas Soome 			return;
390d7802caeSToomas Soome 		}
391d7802caeSToomas Soome 		ret = mkdir(file,
392d7802caeSToomas Soome 		    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
393d7802caeSToomas Soome 		if (errno == EEXIST)
394d7802caeSToomas Soome 			ret = 0;
395d7802caeSToomas Soome 	}
396d7802caeSToomas Soome 	free(file);
397d7802caeSToomas Soome 	if (ret < 0) {
398d7802caeSToomas Soome 		perror("mkdir");
399d7802caeSToomas Soome 		return;
4000c946d80SToomas Soome 	}
4010c946d80SToomas Soome 
402d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
403d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
404d7802caeSToomas Soome 		return;
405d7802caeSToomas Soome 	}
406d7802caeSToomas Soome 
407d7802caeSToomas Soome 	/* Write stage file. Should create temp file and rename. */
408d7802caeSToomas Soome 	(void) chmod(file, S_IRUSR | S_IWUSR);
409d7802caeSToomas Soome 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
410d7802caeSToomas Soome 	if (fd != -1) {
411d7802caeSToomas Soome 		ret = write_out(fd, bblock->buf, bblock->buf_size, 0);
412d7802caeSToomas Soome 		if (ret == BC_SUCCESS) {
413d7802caeSToomas Soome 			(void) fprintf(stdout,
414d7802caeSToomas Soome 			    gettext("bootblock written to %s\n"), file);
415d7802caeSToomas Soome 		} else {
416d7802caeSToomas Soome 			(void) fprintf(stdout,
417d7802caeSToomas Soome 			    gettext("error while writing %s\n"), file);
418d7802caeSToomas Soome 		}
419d7802caeSToomas Soome 		(void) fchmod(fd, S_IRUSR | S_IRGRP | S_IROTH);
420d7802caeSToomas Soome 		(void) close(fd);
421d7802caeSToomas Soome 	}
422d7802caeSToomas Soome 	free(file);
423d7802caeSToomas Soome }
424d7802caeSToomas Soome 
425d7802caeSToomas Soome /*
426d7802caeSToomas Soome  * MBR setup only depends on write_mbr toggle.
427d7802caeSToomas Soome  */
428d7802caeSToomas Soome static bool
429d7802caeSToomas Soome compare_mbr_cb(struct partlist *plist)
430d7802caeSToomas Soome {
431d7802caeSToomas Soome 	/* get confirmation for -m */
432d7802caeSToomas Soome 	if (write_mbr && !force_mbr) {
433d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("Updating master boot sector "
434d7802caeSToomas Soome 		    "destroys existing boot managers (if any).\n"
435d7802caeSToomas Soome 		    "continue (y/n)? "));
436d7802caeSToomas Soome 		if (!yes()) {
437d7802caeSToomas Soome 			write_mbr = false;
438d7802caeSToomas Soome 			(void) fprintf(stdout, gettext("master boot sector "
439d7802caeSToomas Soome 			    "not updated\n"));
440d7802caeSToomas Soome 		}
441d7802caeSToomas Soome 	}
442d7802caeSToomas Soome 	if (write_mbr)
443d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
444d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
445d7802caeSToomas Soome 	return (write_mbr);
446d7802caeSToomas Soome }
447d7802caeSToomas Soome 
448d7802caeSToomas Soome /*
449d7802caeSToomas Soome  * VBR setup is always done.
450d7802caeSToomas Soome  */
451d7802caeSToomas Soome static bool
452d7802caeSToomas Soome compare_stage1_cb(struct partlist *plist)
453d7802caeSToomas Soome {
454d7802caeSToomas Soome 	(void) printf("%s will be written to %s\n", plist->pl_src_name,
455d7802caeSToomas Soome 	    plist->pl_devname);
456d7802caeSToomas Soome 	return (true);
457d7802caeSToomas Soome }
458d7802caeSToomas Soome 
459d7802caeSToomas Soome /*
460d7802caeSToomas Soome  * Return true if we can update, false if not.
461d7802caeSToomas Soome  */
462d7802caeSToomas Soome static bool
463d7802caeSToomas Soome compare_einfo_cb(struct partlist *plist)
464d7802caeSToomas Soome {
465d7802caeSToomas Soome 	ib_bootblock_t *bblock, *bblock_file;
466d7802caeSToomas Soome 	bblk_einfo_t *einfo, *einfo_file;
467d7802caeSToomas Soome 	bblk_hs_t bblock_hs;
468d7802caeSToomas Soome 	bool rv;
469d7802caeSToomas Soome 
470*46d70dceSToomas Soome 	bblock_file = plist->pl_src_data;
471*46d70dceSToomas Soome 	if (bblock_file == NULL)
472*46d70dceSToomas Soome 		return (false);	/* source is missing, cannot update */
473*46d70dceSToomas Soome 
474d7802caeSToomas Soome 	bblock = plist->pl_stage;
475d7802caeSToomas Soome 	if (bblock == NULL || bblock->extra == NULL || bblock->extra_size == 0)
476d7802caeSToomas Soome 		return (true);
477d7802caeSToomas Soome 
478d7802caeSToomas Soome 	einfo = find_einfo(bblock->extra, bblock->extra_size);
479d7802caeSToomas Soome 	if (einfo == NULL) {
480d7802caeSToomas Soome 		BOOT_DEBUG("No extended information available on disk\n");
481d7802caeSToomas Soome 		return (true);
482d7802caeSToomas Soome 	}
483d7802caeSToomas Soome 
484d7802caeSToomas Soome 	einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
485d7802caeSToomas Soome 	if (einfo_file == NULL) {
486d7802caeSToomas Soome 		/*
487d7802caeSToomas Soome 		 * loader bootblock is versioned. missing version means
488d7802caeSToomas Soome 		 * probably incompatible block. installboot can not install
489d7802caeSToomas Soome 		 * grub, for example.
490d7802caeSToomas Soome 		 */
4910c946d80SToomas Soome 		(void) fprintf(stderr,
492d7802caeSToomas Soome 		    gettext("ERROR: non versioned bootblock in file\n"));
493d7802caeSToomas Soome 		return (false);
494d7802caeSToomas Soome 	} else {
495d7802caeSToomas Soome 		if (update_str == NULL) {
496d7802caeSToomas Soome 			update_str = einfo_get_string(einfo_file);
497d7802caeSToomas Soome 			do_version = true;
498d7802caeSToomas Soome 		}
4990c946d80SToomas Soome 	}
5000c946d80SToomas Soome 
501d7802caeSToomas Soome 	if (!do_version || update_str == NULL) {
502d7802caeSToomas Soome 		(void) fprintf(stderr,
503d7802caeSToomas Soome 		    gettext("WARNING: target device %s has a "
504d7802caeSToomas Soome 		    "versioned bootblock that is going to be overwritten by a "
505d7802caeSToomas Soome 		    "non versioned one\n"), plist->pl_devname);
506d7802caeSToomas Soome 		return (true);
507d7802caeSToomas Soome 	}
5080c946d80SToomas Soome 
509d7802caeSToomas Soome 	if (force_update) {
510d7802caeSToomas Soome 		BOOT_DEBUG("Forcing update of %s bootblock\n",
511d7802caeSToomas Soome 		    plist->pl_devname);
512d7802caeSToomas Soome 		return (true);
513d7802caeSToomas Soome 	}
5140c946d80SToomas Soome 
515d7802caeSToomas Soome 	BOOT_DEBUG("Ready to check installed version vs %s\n", update_str);
5160c946d80SToomas Soome 
517d7802caeSToomas Soome 	bblock_hs.src_buf = (unsigned char *)bblock_file->file;
518d7802caeSToomas Soome 	bblock_hs.src_size = bblock_file->file_size;
519d7802caeSToomas Soome 
520d7802caeSToomas Soome 	rv = einfo_should_update(einfo, &bblock_hs, update_str);
521d7802caeSToomas Soome 	if (rv == false) {
522d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("\nBootblock version installed "
523d7802caeSToomas Soome 		    "on %s is more recent or identical to\n%s\n"
524d7802caeSToomas Soome 		    "Use -F to override or install without the -u option.\n"),
525d7802caeSToomas Soome 		    plist->pl_devname, plist->pl_src_name);
526d7802caeSToomas Soome 	} else {
527d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
528d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
529d7802caeSToomas Soome 	}
530d7802caeSToomas Soome 	return (rv);
531d7802caeSToomas Soome }
532d7802caeSToomas Soome 
533d7802caeSToomas Soome static bool
534d7802caeSToomas Soome read_stage1_cb(struct partlist *plist)
535d7802caeSToomas Soome {
536d7802caeSToomas Soome 	int fd;
537d7802caeSToomas Soome 	bool rv = false;
538d7802caeSToomas Soome 
539d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
540d7802caeSToomas Soome 		return (rv);
541d7802caeSToomas Soome 
542d7802caeSToomas Soome 	if (plist->pl_stage == NULL)
543d7802caeSToomas Soome 		plist->pl_stage = calloc(1, sector_size);
544d7802caeSToomas Soome 
545d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
546d7802caeSToomas Soome 		perror("calloc");
547d7802caeSToomas Soome 		goto done;
548d7802caeSToomas Soome 	}
5490c946d80SToomas Soome 
550d7802caeSToomas Soome 	if (pread(fd, plist->pl_stage, sector_size, 0) == -1) {
551d7802caeSToomas Soome 		perror("pread");
552d7802caeSToomas Soome 		goto done;
553d7802caeSToomas Soome 	}
554d7802caeSToomas Soome 	rv = true;
555d7802caeSToomas Soome done:
5560c946d80SToomas Soome 	(void) close(fd);
557d7802caeSToomas Soome 	return (rv);
558d7802caeSToomas Soome }
5590c946d80SToomas Soome 
560d7802caeSToomas Soome static bool
561d7802caeSToomas Soome read_stage1_bbl_cb(struct partlist *plist)
562d7802caeSToomas Soome {
563d7802caeSToomas Soome 	int fd;
564d7802caeSToomas Soome 	void *data;
565d7802caeSToomas Soome 	bool rv = false;
566d7802caeSToomas Soome 
567d7802caeSToomas Soome 	data = malloc(SECTOR_SIZE);
568d7802caeSToomas Soome 	if (data == NULL)
569d7802caeSToomas Soome 		return (rv);
570d7802caeSToomas Soome 
571d7802caeSToomas Soome 	/* read the stage1 file from filesystem */
572d7802caeSToomas Soome 	fd = open(plist->pl_src_name, O_RDONLY);
573d7802caeSToomas Soome 	if (fd == -1 ||
574d7802caeSToomas Soome 	    read(fd, data, SECTOR_SIZE) != SECTOR_SIZE) {
575d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
576d7802caeSToomas Soome 		    plist->pl_src_name);
577d7802caeSToomas Soome 		free(data);
578d7802caeSToomas Soome 		if (fd != -1)
579d7802caeSToomas Soome 			(void) close(fd);
580d7802caeSToomas Soome 		return (rv);
581d7802caeSToomas Soome 	}
582d7802caeSToomas Soome 
583d7802caeSToomas Soome 	plist->pl_src_data = data;
5840c946d80SToomas Soome 	(void) close(fd);
585d7802caeSToomas Soome 	return (true);
5860c946d80SToomas Soome }
5870c946d80SToomas Soome 
588d7802caeSToomas Soome static bool
589d7802caeSToomas Soome read_stage2_cb(struct partlist *plist)
5900c946d80SToomas Soome {
591d7802caeSToomas Soome 	ib_device_t		*device;
592d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
593d7802caeSToomas Soome 	int			fd;
5940c946d80SToomas Soome 	uint32_t		size, offset;
5950c946d80SToomas Soome 	uint32_t		buf_size;
5960c946d80SToomas Soome 	uint32_t		mboot_off;
5970c946d80SToomas Soome 	multiboot_header_t	*mboot;
5980c946d80SToomas Soome 
599d7802caeSToomas Soome 	bblock = calloc(1, sizeof (ib_bootblock_t));
600d7802caeSToomas Soome 	if (bblock == NULL)
601d7802caeSToomas Soome 		return (false);
6020c946d80SToomas Soome 
603d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
604d7802caeSToomas Soome 		free(bblock);
605d7802caeSToomas Soome 		return (false);
6060c946d80SToomas Soome 	}
6070c946d80SToomas Soome 
608d7802caeSToomas Soome 	device = plist->pl_device;
609d7802caeSToomas Soome 	plist->pl_stage = bblock;
610d7802caeSToomas Soome 	offset = device->stage.offset * SECTOR_SIZE;
611d7802caeSToomas Soome 
612d7802caeSToomas Soome 	if (read_in(fd, mboot_scan, sizeof (mboot_scan), offset)
6130c946d80SToomas Soome 	    != BC_SUCCESS) {
6140c946d80SToomas Soome 		BOOT_DEBUG("Error reading bootblock area\n");
6150c946d80SToomas Soome 		perror("read");
616d7802caeSToomas Soome 		(void) close(fd);
617d7802caeSToomas Soome 		return (false);
6180c946d80SToomas Soome 	}
6190c946d80SToomas Soome 
6200c946d80SToomas Soome 	/* No multiboot means no chance of knowing bootblock size */
6210c946d80SToomas Soome 	if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
6220c946d80SToomas Soome 	    != BC_SUCCESS) {
6230c946d80SToomas Soome 		BOOT_DEBUG("Unable to find multiboot header\n");
624d7802caeSToomas Soome 		(void) close(fd);
625d7802caeSToomas Soome 		return (false);
6260c946d80SToomas Soome 	}
6270c946d80SToomas Soome 	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
6280c946d80SToomas Soome 
6290c946d80SToomas Soome 	/*
6300c946d80SToomas Soome 	 * make sure mboot has sane values
6310c946d80SToomas Soome 	 */
6320c946d80SToomas Soome 	if (mboot->load_end_addr == 0 ||
633d7802caeSToomas Soome 	    mboot->load_end_addr < mboot->load_addr) {
634d7802caeSToomas Soome 		(void) close(fd);
635d7802caeSToomas Soome 		return (false);
636d7802caeSToomas Soome 	}
6370c946d80SToomas Soome 
6380c946d80SToomas Soome 	/*
6390c946d80SToomas Soome 	 * Currently, the amount of space reserved for extra information
6400c946d80SToomas Soome 	 * is "fixed". We may have to scan for the terminating extra payload
6410c946d80SToomas Soome 	 * in the future.
6420c946d80SToomas Soome 	 */
6430c946d80SToomas Soome 	size = mboot->load_end_addr - mboot->load_addr;
6440c946d80SToomas Soome 	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
6450c946d80SToomas Soome 	bblock->file_size = size;
6460c946d80SToomas Soome 
6470c946d80SToomas Soome 	bblock->buf = malloc(buf_size);
6480c946d80SToomas Soome 	if (bblock->buf == NULL) {
6490c946d80SToomas Soome 		BOOT_DEBUG("Unable to allocate enough memory to read"
6500c946d80SToomas Soome 		    " the extra bootblock from the disk\n");
6510c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
652d7802caeSToomas Soome 		(void) close(fd);
653d7802caeSToomas Soome 		return (false);
6540c946d80SToomas Soome 	}
6550c946d80SToomas Soome 	bblock->buf_size = buf_size;
6560c946d80SToomas Soome 
657d7802caeSToomas Soome 	if (read_in(fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
6580c946d80SToomas Soome 		BOOT_DEBUG("Error reading the bootblock\n");
6590c946d80SToomas Soome 		(void) free(bblock->buf);
6600c946d80SToomas Soome 		bblock->buf = NULL;
661d7802caeSToomas Soome 		(void) close(fd);
662d7802caeSToomas Soome 		return (false);
6630c946d80SToomas Soome 	}
6640c946d80SToomas Soome 
6650c946d80SToomas Soome 	/* Update pointers. */
6660c946d80SToomas Soome 	bblock->file = bblock->buf;
6670c946d80SToomas Soome 	bblock->mboot_off = mboot_off;
6680c946d80SToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
6690c946d80SToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
6700c946d80SToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
6710c946d80SToomas Soome 
6720c946d80SToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
6730c946d80SToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
6740c946d80SToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
6750c946d80SToomas Soome 
676d7802caeSToomas Soome 	return (true);
6770c946d80SToomas Soome }
6780c946d80SToomas Soome 
679d7802caeSToomas Soome static bool
680d7802caeSToomas Soome read_einfo_file_cb(struct partlist *plist)
6810c946d80SToomas Soome {
682*46d70dceSToomas Soome 	int rc;
683*46d70dceSToomas Soome 	void *stage;
684*46d70dceSToomas Soome 
685*46d70dceSToomas Soome 	stage = calloc(1, sizeof (ib_bootblock_t));
686*46d70dceSToomas Soome 	if (stage == NULL)
687d7802caeSToomas Soome 		return (false);
6880c946d80SToomas Soome 
689*46d70dceSToomas Soome 	rc =  read_bootblock_from_file(plist->pl_devname, stage);
690*46d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
691*46d70dceSToomas Soome 		free(stage);
692*46d70dceSToomas Soome 		stage = NULL;
693*46d70dceSToomas Soome 	}
694*46d70dceSToomas Soome 	plist->pl_stage = stage;
695*46d70dceSToomas Soome 	return (rc == BC_SUCCESS);
696d7802caeSToomas Soome }
6970c946d80SToomas Soome 
698d7802caeSToomas Soome static bool
699d7802caeSToomas Soome read_stage2_file_cb(struct partlist *plist)
700d7802caeSToomas Soome {
701*46d70dceSToomas Soome 	int rc;
702*46d70dceSToomas Soome 	void *data;
703*46d70dceSToomas Soome 
704*46d70dceSToomas Soome 	data = calloc(1, sizeof (ib_bootblock_t));
705*46d70dceSToomas Soome 	if (data == NULL)
706d7802caeSToomas Soome 		return (false);
7070c946d80SToomas Soome 
708*46d70dceSToomas Soome 	rc = read_bootblock_from_file(plist->pl_src_name, data);
709*46d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
710*46d70dceSToomas Soome 		free(data);
711*46d70dceSToomas Soome 		data = NULL;
712*46d70dceSToomas Soome 	}
713*46d70dceSToomas Soome 	plist->pl_src_data = data;
714*46d70dceSToomas Soome 	return (rc == BC_SUCCESS);
715d7802caeSToomas Soome }
7160c946d80SToomas Soome 
717d7802caeSToomas Soome /*
718d7802caeSToomas Soome  * convert /dev/rdsk/... to /dev/dsk/...
719d7802caeSToomas Soome  */
720d7802caeSToomas Soome static char *
721d7802caeSToomas Soome make_blkdev(const char *path)
722d7802caeSToomas Soome {
723d7802caeSToomas Soome 	char *tmp;
724d7802caeSToomas Soome 	char *ptr = strdup(path);
7250c946d80SToomas Soome 
726d7802caeSToomas Soome 	if (ptr == NULL)
727d7802caeSToomas Soome 		return (ptr);
7280c946d80SToomas Soome 
729d7802caeSToomas Soome 	tmp = strstr(ptr, "rdsk");
730d7802caeSToomas Soome 	if (tmp == NULL) {
731d7802caeSToomas Soome 		free(ptr);
732d7802caeSToomas Soome 		return (NULL); /* Something is very wrong */
7330c946d80SToomas Soome 	}
734d7802caeSToomas Soome 	/* This is safe because we do shorten the string */
735d7802caeSToomas Soome 	(void) memmove(tmp, tmp + 1, strlen(tmp));
736d7802caeSToomas Soome 	return (ptr);
737d7802caeSToomas Soome }
7380c946d80SToomas Soome 
739d7802caeSToomas Soome /*
740d7802caeSToomas Soome  * Try to mount ESP and read boot program.
741d7802caeSToomas Soome  */
742d7802caeSToomas Soome static bool
743d7802caeSToomas Soome read_einfo_esp_cb(struct partlist *plist)
744d7802caeSToomas Soome {
745d7802caeSToomas Soome 	fstyp_handle_t fhdl;
746d7802caeSToomas Soome 	const char *fident;
747d7802caeSToomas Soome 	char *blkdev, *path, *file;
748d7802caeSToomas Soome 	bool rv = false;
749d7802caeSToomas Soome 	FILE *fp;
750d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
751d7802caeSToomas Soome 	int fd, ret;
7520c946d80SToomas Soome 
753d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
754d7802caeSToomas Soome 		return (rv);
7550c946d80SToomas Soome 
756d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
757d7802caeSToomas Soome 		(void) close(fd);
758d7802caeSToomas Soome 		return (rv);
759d7802caeSToomas Soome 	}
7600c946d80SToomas Soome 
761d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
762d7802caeSToomas Soome 		fstyp_fini(fhdl);
763d7802caeSToomas Soome 		(void) close(fd);
764d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
765d7802caeSToomas Soome 		    "system type\n"));
766d7802caeSToomas Soome 		return (rv);
767d7802caeSToomas Soome 	}
7680c946d80SToomas Soome 
769d7802caeSToomas Soome 	/* We only do expect pcfs. */
770d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_PCFS) != 0) {
771d7802caeSToomas Soome 		(void) fprintf(stderr,
772d7802caeSToomas Soome 		    gettext("File system %s is not supported.\n"), fident);
773d7802caeSToomas Soome 		fstyp_fini(fhdl);
774d7802caeSToomas Soome 		(void) close(fd);
775d7802caeSToomas Soome 		return (rv);
776d7802caeSToomas Soome 	}
777d7802caeSToomas Soome 	fstyp_fini(fhdl);
778d7802caeSToomas Soome 	(void) close(fd);
779d7802caeSToomas Soome 
780d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
781d7802caeSToomas Soome 	if (blkdev == NULL)
782d7802caeSToomas Soome 		return (rv);
783d7802caeSToomas Soome 
784d7802caeSToomas Soome 	/* mount ESP if needed, read boot program(s) and unmount. */
785d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
786d7802caeSToomas Soome 	if (fp == NULL) {
787d7802caeSToomas Soome 		perror("fopen");
788d7802caeSToomas Soome 		free(blkdev);
789d7802caeSToomas Soome 		return (rv);
7900c946d80SToomas Soome 	}
7910c946d80SToomas Soome 
792d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
793d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
794d7802caeSToomas Soome 	(void) fclose(fp);
795d7802caeSToomas Soome 	if (ret == 0)
796d7802caeSToomas Soome 		path = mp.mnt_mountp;
797d7802caeSToomas Soome 	else
798d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
799d7802caeSToomas Soome 
800d7802caeSToomas Soome 	free(blkdev);
801d7802caeSToomas Soome 	if (path == NULL)
802d7802caeSToomas Soome 		return (rv);
803d7802caeSToomas Soome 
804d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
805d7802caeSToomas Soome 		return (rv);
806d7802caeSToomas Soome 	}
807d7802caeSToomas Soome 
808d7802caeSToomas Soome 	plist->pl_stage = calloc(1, sizeof (ib_bootblock_t));
809d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
810d7802caeSToomas Soome 		free(file);
811d7802caeSToomas Soome 		return (rv);
812d7802caeSToomas Soome 	}
813d7802caeSToomas Soome 	if (read_bootblock_from_file(file, plist->pl_stage) != BC_SUCCESS) {
814d7802caeSToomas Soome 		free(plist->pl_stage);
815d7802caeSToomas Soome 		plist->pl_stage = NULL;
816d7802caeSToomas Soome 	} else {
817d7802caeSToomas Soome 		rv = true;
818d7802caeSToomas Soome 	}
819d7802caeSToomas Soome 
820d7802caeSToomas Soome 	free(file);
821d7802caeSToomas Soome 	return (rv);
8220c946d80SToomas Soome }
8230c946d80SToomas Soome 
824d7802caeSToomas Soome static void
825d7802caeSToomas Soome print_stage1_cb(struct partlist *plist)
8260c946d80SToomas Soome {
827d7802caeSToomas Soome 	struct mboot *mbr;
828d7802caeSToomas Soome 	struct ipart *part;
829d7802caeSToomas Soome 	mbr_type_t type = MBR_TYPE_UNKNOWN;
830d7802caeSToomas Soome 	bool pmbr = false;
831d7802caeSToomas Soome 	char *label;
8320c946d80SToomas Soome 
833d7802caeSToomas Soome 	mbr = plist->pl_stage;
8340c946d80SToomas Soome 
835d7802caeSToomas Soome 	if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
836d7802caeSToomas Soome 		type = MBR_TYPE_GRUB1;
837d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
838d7802caeSToomas Soome 		type = MBR_TYPE_LOADER;
839d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
840d7802caeSToomas Soome 		type = MBR_TYPE_LOADER_JOYENT;
841d7802caeSToomas Soome 	}
8420c946d80SToomas Soome 
843d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
844d7802caeSToomas Soome 	for (int i = 0; i < FD_NUMPART; i++) {
845d7802caeSToomas Soome 		if (part[i].systid == EFI_PMBR)
846d7802caeSToomas Soome 			pmbr = true;
847d7802caeSToomas Soome 	}
8480c946d80SToomas Soome 
849d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR)
850d7802caeSToomas Soome 		label = pmbr ? "PMBR" : "MBR";
851d7802caeSToomas Soome 	else
852d7802caeSToomas Soome 		label = "VBR";
8530c946d80SToomas Soome 
854d7802caeSToomas Soome 	printf("%s block from %s:\n", label, plist->pl_devname);
8550c946d80SToomas Soome 
856d7802caeSToomas Soome 	switch (type) {
857d7802caeSToomas Soome 	case MBR_TYPE_UNKNOWN:
858d7802caeSToomas Soome 		printf("Format: unknown\n");
859d7802caeSToomas Soome 		break;
860d7802caeSToomas Soome 	case MBR_TYPE_GRUB1:
861d7802caeSToomas Soome 		printf("Format: grub1\n");
862d7802caeSToomas Soome 		break;
863d7802caeSToomas Soome 	case MBR_TYPE_LOADER:
864d7802caeSToomas Soome 		printf("Format: loader (illumos)\n");
865d7802caeSToomas Soome 		break;
866d7802caeSToomas Soome 	case MBR_TYPE_LOADER_JOYENT:
867d7802caeSToomas Soome 		printf("Format: loader (joyent)\n");
868d7802caeSToomas Soome 		break;
8690c946d80SToomas Soome 	}
8700c946d80SToomas Soome 
871d7802caeSToomas Soome 	printf("Signature: 0x%hx (%s)\n", mbr->signature,
872d7802caeSToomas Soome 	    mbr->signature == MBB_MAGIC ? "valid" : "invalid");
8730c946d80SToomas Soome 
874d7802caeSToomas Soome 	printf("UniqueMBRDiskSignature: %#lx\n",
875d7802caeSToomas Soome 	    *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
876d7802caeSToomas Soome 
877d7802caeSToomas Soome 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
878d7802caeSToomas Soome 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
879d7802caeSToomas Soome 
880d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_LBA: %llu\n",
881d7802caeSToomas Soome 		    *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
882d7802caeSToomas Soome 
883d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
884d7802caeSToomas Soome 		    *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
885d7802caeSToomas Soome 
886d7802caeSToomas Soome 		uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
887d7802caeSToomas Soome 		    uuid);
888d7802caeSToomas Soome 
889d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
890d7802caeSToomas Soome 	}
891d7802caeSToomas Soome 	printf("\n");
8920c946d80SToomas Soome }
8930c946d80SToomas Soome 
894d7802caeSToomas Soome static void
895d7802caeSToomas Soome print_einfo_cb(struct partlist *plist)
8960c946d80SToomas Soome {
897d7802caeSToomas Soome 	uint8_t flags = 0;
898d7802caeSToomas Soome 	ib_bootblock_t *bblock;
899d7802caeSToomas Soome 	bblk_einfo_t *einfo = NULL;
900d7802caeSToomas Soome 	const char *filepath;
901d7802caeSToomas Soome 
902d7802caeSToomas Soome 	/* No stage, get out. */
903d7802caeSToomas Soome 	bblock = plist->pl_stage;
904d7802caeSToomas Soome 	if (bblock == NULL)
905d7802caeSToomas Soome 		return;
9060c946d80SToomas Soome 
907d7802caeSToomas Soome 	if (plist->pl_device->stage.path == NULL)
908d7802caeSToomas Soome 		filepath = "";
909d7802caeSToomas Soome 	else
910d7802caeSToomas Soome 		filepath = plist->pl_device->stage.path;
9110c946d80SToomas Soome 
912d7802caeSToomas Soome 	printf("Boot block from %s:%s\n", plist->pl_devname, filepath);
9130c946d80SToomas Soome 
914d7802caeSToomas Soome 	if (bblock->extra != NULL)
915d7802caeSToomas Soome 		einfo = find_einfo(bblock->extra, bblock->extra_size);
9160c946d80SToomas Soome 
917d7802caeSToomas Soome 	if (einfo == NULL) {
918d7802caeSToomas Soome 		(void) fprintf(stderr,
919d7802caeSToomas Soome 		    gettext("No extended information found.\n\n"));
920d7802caeSToomas Soome 		return;
921d7802caeSToomas Soome 	}
9220c946d80SToomas Soome 
923d7802caeSToomas Soome 	/* Print the extended information. */
924d7802caeSToomas Soome 	if (strip)
925d7802caeSToomas Soome 		flags |= EINFO_EASY_PARSE;
926d7802caeSToomas Soome 	if (verbose_dump)
927d7802caeSToomas Soome 		flags |= EINFO_PRINT_HEADER;
928d7802caeSToomas Soome 
929d7802caeSToomas Soome 	print_einfo(flags, einfo, bblock->extra_size);
930d7802caeSToomas Soome 	printf("\n");
9310c946d80SToomas Soome }
9320c946d80SToomas Soome 
933d7802caeSToomas Soome static size_t
934d7802caeSToomas Soome get_media_info(int fd)
9350c946d80SToomas Soome {
936d7802caeSToomas Soome 	struct dk_minfo disk_info;
9370c946d80SToomas Soome 
938d7802caeSToomas Soome 	if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
939d7802caeSToomas Soome 		return (SECTOR_SIZE);
9400c946d80SToomas Soome 
941d7802caeSToomas Soome 	return (disk_info.dki_lbsize);
942d7802caeSToomas Soome }
943d7802caeSToomas Soome 
944d7802caeSToomas Soome static struct partlist *
945d7802caeSToomas Soome partlist_alloc(void)
946d7802caeSToomas Soome {
947d7802caeSToomas Soome 	struct partlist *pl;
948d7802caeSToomas Soome 
949d7802caeSToomas Soome 	if ((pl = calloc(1, sizeof (*pl))) == NULL) {
950d7802caeSToomas Soome 		perror("calloc");
951d7802caeSToomas Soome 		return (NULL);
9520c946d80SToomas Soome 	}
9530c946d80SToomas Soome 
954d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
955d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
956d7802caeSToomas Soome 		perror("calloc");
957d7802caeSToomas Soome 		free(pl);
958d7802caeSToomas Soome 		return (NULL);
959d7802caeSToomas Soome 	}
960d7802caeSToomas Soome 
961d7802caeSToomas Soome 	return (pl);
962d7802caeSToomas Soome }
963d7802caeSToomas Soome 
964d7802caeSToomas Soome static void
965d7802caeSToomas Soome partlist_free(struct partlist *pl)
966d7802caeSToomas Soome {
967d7802caeSToomas Soome 	ib_bootblock_t *bblock;
968d7802caeSToomas Soome 	ib_device_t *device;
969d7802caeSToomas Soome 
970d7802caeSToomas Soome 	switch (pl->pl_type) {
971d7802caeSToomas Soome 	case IB_BBLK_MBR:
972d7802caeSToomas Soome 	case IB_BBLK_STAGE1:
973d7802caeSToomas Soome 		free(pl->pl_stage);
974d7802caeSToomas Soome 		break;
975d7802caeSToomas Soome 	default:
976d7802caeSToomas Soome 		if (pl->pl_stage != NULL) {
977d7802caeSToomas Soome 			bblock = pl->pl_stage;
978d7802caeSToomas Soome 			free(bblock->buf);
979d7802caeSToomas Soome 			free(bblock);
9800c946d80SToomas Soome 		}
9810c946d80SToomas Soome 	}
982d7802caeSToomas Soome 
983d7802caeSToomas Soome 	/* umount the stage fs. */
984d7802caeSToomas Soome 	if (pl->pl_device->stage.mntpnt != NULL) {
985d7802caeSToomas Soome 		if (umount(pl->pl_device->stage.mntpnt) == 0)
986d7802caeSToomas Soome 			(void) rmdir(pl->pl_device->stage.mntpnt);
987d7802caeSToomas Soome 		free(pl->pl_device->stage.mntpnt);
9880c946d80SToomas Soome 	}
989d7802caeSToomas Soome 	device = pl->pl_device;
990d7802caeSToomas Soome 	free(device->target.path);
991d7802caeSToomas Soome 	free(pl->pl_device);
9920c946d80SToomas Soome 
993d7802caeSToomas Soome 	free(pl->pl_src_data);
994d7802caeSToomas Soome 	free(pl->pl_devname);
995d7802caeSToomas Soome 	free(pl);
996d7802caeSToomas Soome }
9970c946d80SToomas Soome 
998d7802caeSToomas Soome static bool
999d7802caeSToomas Soome probe_fstyp(ib_data_t *data)
1000d7802caeSToomas Soome {
1001d7802caeSToomas Soome 	fstyp_handle_t fhdl;
1002d7802caeSToomas Soome 	const char *fident;
1003d7802caeSToomas Soome 	char *ptr;
1004d7802caeSToomas Soome 	int fd;
1005d7802caeSToomas Soome 	bool rv = false;
1006d7802caeSToomas Soome 
1007d7802caeSToomas Soome 	/* Record partition id */
1008d7802caeSToomas Soome 	ptr = strrchr(data->target.path, 'p');
1009d7802caeSToomas Soome 	if (ptr == NULL)
1010d7802caeSToomas Soome 		ptr = strrchr(data->target.path, 's');
1011d7802caeSToomas Soome 	data->target.id = atoi(++ptr);
1012d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) == -1)
1013d7802caeSToomas Soome 		return (rv);
1014d7802caeSToomas Soome 
1015d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
1016d7802caeSToomas Soome 		(void) close(fd);
1017d7802caeSToomas Soome 		return (rv);
1018d7802caeSToomas Soome 	}
1019d7802caeSToomas Soome 
1020d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1021d7802caeSToomas Soome 		fstyp_fini(fhdl);
1022d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
1023d7802caeSToomas Soome 		    "system type\n"));
1024d7802caeSToomas Soome 		(void) close(fd);
1025d7802caeSToomas Soome 		return (rv);
1026d7802caeSToomas Soome 	}
1027d7802caeSToomas Soome 
1028d7802caeSToomas Soome 	rv = true;
1029d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_ZFS) == 0)
1030d7802caeSToomas Soome 		data->target.fstype = IB_FS_ZFS;
1031d7802caeSToomas Soome 	else if (strcmp(fident, MNTTYPE_UFS) == 0) {
1032d7802caeSToomas Soome 		data->target.fstype = IB_FS_UFS;
1033d7802caeSToomas Soome 	} else if (strcmp(fident, MNTTYPE_PCFS) == 0) {
1034d7802caeSToomas Soome 		data->target.fstype = IB_FS_PCFS;
1035d7802caeSToomas Soome 	} else {
1036d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("File system %s is not "
1037d7802caeSToomas Soome 		    "supported by loader\n"), fident);
1038d7802caeSToomas Soome 		rv = false;
1039d7802caeSToomas Soome 	}
1040d7802caeSToomas Soome 	fstyp_fini(fhdl);
1041d7802caeSToomas Soome 	(void) close(fd);
1042d7802caeSToomas Soome 	return (rv);
10430c946d80SToomas Soome }
10440c946d80SToomas Soome 
1045d7802caeSToomas Soome static bool
1046d7802caeSToomas Soome get_slice(ib_data_t *data, struct partlist *pl, struct dk_gpt *vtoc,
1047d7802caeSToomas Soome     uint16_t tag)
10480c946d80SToomas Soome {
1049d7802caeSToomas Soome 	uint_t i;
1050d7802caeSToomas Soome 	ib_device_t *device = pl->pl_device;
1051d7802caeSToomas Soome 	char *path, *ptr;
10520c946d80SToomas Soome 
1053d7802caeSToomas Soome 	if (tag != V_BOOT && tag != V_SYSTEM)
1054d7802caeSToomas Soome 		return (false);
10550c946d80SToomas Soome 
1056d7802caeSToomas Soome 	for (i = 0; i < vtoc->efi_nparts; i++) {
1057d7802caeSToomas Soome 		if (vtoc->efi_parts[i].p_tag == tag) {
1058d7802caeSToomas Soome 			if ((path = strdup(data->target.path)) == NULL) {
1059d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1060d7802caeSToomas Soome 				return (false);
1061d7802caeSToomas Soome 			}
1062d7802caeSToomas Soome 			ptr = strrchr(path, 's');
1063d7802caeSToomas Soome 			ptr++;
1064d7802caeSToomas Soome 			*ptr = '\0';
1065d7802caeSToomas Soome 			(void) asprintf(&ptr, "%s%d", path, i);
1066d7802caeSToomas Soome 			free(path);
1067d7802caeSToomas Soome 			if (ptr == NULL) {
1068d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1069d7802caeSToomas Soome 				return (false);
1070d7802caeSToomas Soome 			}
1071d7802caeSToomas Soome 			pl->pl_devname = ptr;
1072d7802caeSToomas Soome 			device->stage.id = i;
1073d7802caeSToomas Soome 			device->stage.devtype = IB_DEV_EFI;
1074d7802caeSToomas Soome 			switch (vtoc->efi_parts[i].p_tag) {
1075d7802caeSToomas Soome 			case V_BOOT:
1076d7802caeSToomas Soome 				device->stage.fstype = IB_FS_NONE;
1077d7802caeSToomas Soome 				/* leave sector 0 for VBR */
1078d7802caeSToomas Soome 				device->stage.offset = 1;
1079d7802caeSToomas Soome 				break;
1080d7802caeSToomas Soome 			case V_SYSTEM:
1081d7802caeSToomas Soome 				device->stage.fstype = IB_FS_PCFS;
1082d7802caeSToomas Soome 				break;
1083d7802caeSToomas Soome 			}
1084d7802caeSToomas Soome 			device->stage.tag = vtoc->efi_parts[i].p_tag;
1085d7802caeSToomas Soome 			device->stage.start = vtoc->efi_parts[i].p_start;
1086d7802caeSToomas Soome 			device->stage.size = vtoc->efi_parts[i].p_size;
1087d7802caeSToomas Soome 			break;
10880c946d80SToomas Soome 		}
10890c946d80SToomas Soome 	}
1090d7802caeSToomas Soome 	return (true);
1091d7802caeSToomas Soome }
10920c946d80SToomas Soome 
1093d7802caeSToomas Soome static bool
1094d7802caeSToomas Soome allocate_slice(ib_data_t *data, struct dk_gpt *vtoc, uint16_t tag,
1095d7802caeSToomas Soome     struct partlist **plp)
1096d7802caeSToomas Soome {
1097d7802caeSToomas Soome 	struct partlist *pl;
10980c946d80SToomas Soome 
1099d7802caeSToomas Soome 	*plp = NULL;
1100d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1101d7802caeSToomas Soome 		return (false);
11020c946d80SToomas Soome 
1103d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
1104d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
1105d7802caeSToomas Soome 		perror("calloc");
1106d7802caeSToomas Soome 		partlist_free(pl);
1107d7802caeSToomas Soome 		return (false);
1108d7802caeSToomas Soome 	}
1109d7802caeSToomas Soome 	if (!get_slice(data, pl, vtoc, tag)) {
1110d7802caeSToomas Soome 		partlist_free(pl);
1111d7802caeSToomas Soome 		return (false);
11120c946d80SToomas Soome 	}
11130c946d80SToomas Soome 
1114d7802caeSToomas Soome 	/* tag was not found */
1115d7802caeSToomas Soome 	if (pl->pl_devname == NULL)
1116d7802caeSToomas Soome 		partlist_free(pl);
1117d7802caeSToomas Soome 	else
1118d7802caeSToomas Soome 		*plp = pl;
1119d7802caeSToomas Soome 
1120d7802caeSToomas Soome 	return (true);
11210c946d80SToomas Soome }
11220c946d80SToomas Soome 
1123d7802caeSToomas Soome static bool
1124d7802caeSToomas Soome probe_gpt(ib_data_t *data)
11250c946d80SToomas Soome {
1126d7802caeSToomas Soome 	struct partlist *pl;
1127d7802caeSToomas Soome 	struct dk_gpt *vtoc;
1128d7802caeSToomas Soome 	ib_device_t *device;
1129d7802caeSToomas Soome 	int slice, fd;
1130d7802caeSToomas Soome 	bool rv = false;
11310c946d80SToomas Soome 
1132d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1133d7802caeSToomas Soome 		return (rv);
11340c946d80SToomas Soome 
1135d7802caeSToomas Soome 	slice = efi_alloc_and_read(fd, &vtoc);
1136d7802caeSToomas Soome 	(void) close(fd);
1137d7802caeSToomas Soome 	if (slice < 0)
1138d7802caeSToomas Soome 		return (rv);
1139d7802caeSToomas Soome 
1140d7802caeSToomas Soome 	data->device.devtype = IB_DEV_EFI;
1141d7802caeSToomas Soome 	data->target.start = vtoc->efi_parts[slice].p_start;
1142d7802caeSToomas Soome 	data->target.size = vtoc->efi_parts[slice].p_size;
11430c946d80SToomas Soome 
1144d7802caeSToomas Soome 	/* Always update PMBR. */
1145d7802caeSToomas Soome 	force_mbr = 1;
1146d7802caeSToomas Soome 	write_mbr = 1;
1147d7802caeSToomas Soome 
1148d7802caeSToomas Soome 	/*
1149d7802caeSToomas Soome 	 * With GPT we can have boot partition and ESP.
1150d7802caeSToomas Soome 	 * Boot partition can have both stage 1 and stage 2.
1151d7802caeSToomas Soome 	 */
1152d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1153d7802caeSToomas Soome 		goto done;
1154d7802caeSToomas Soome 	if (pl != NULL) {
1155d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1156d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1157d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1158d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1159d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1160d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1161d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1162d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1163d7802caeSToomas Soome 	} else if (data->target.fstype != IB_FS_ZFS) {
1164d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Booting %s from EFI "
1165d7802caeSToomas Soome 		    "labeled disks requires the boot partition.\n"),
1166d7802caeSToomas Soome 		    data->target.fstype == IB_FS_UFS?
1167d7802caeSToomas Soome 		    MNTTYPE_UFS : MNTTYPE_PCFS);
1168d7802caeSToomas Soome 		goto done;
1169d7802caeSToomas Soome 	}
1170d7802caeSToomas Soome 	/* Add stage 2 */
1171d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1172d7802caeSToomas Soome 		goto done;
1173d7802caeSToomas Soome 	if (pl != NULL) {
1174d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1175d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1176d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1177d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1178d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1179d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1180d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1181d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1182d7802caeSToomas Soome 	}
1183d7802caeSToomas Soome 
1184d7802caeSToomas Soome 	/* ESP can have 32- and 64-bit boot code. */
1185d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1186d7802caeSToomas Soome 		goto done;
1187d7802caeSToomas Soome 	if (pl != NULL) {
1188d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTIA32;
1189d7802caeSToomas Soome 		pl->pl_src_name = efi32;
1190d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1191d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1192d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1193d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1194d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1195d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1196d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1197d7802caeSToomas Soome 	}
1198d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1199d7802caeSToomas Soome 		goto done;
1200d7802caeSToomas Soome 	if (pl != NULL) {
1201d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTX64;
1202d7802caeSToomas Soome 		pl->pl_src_name = efi64;
1203d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1204d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1205d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1206d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1207d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1208d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1209d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1210d7802caeSToomas Soome 	}
1211d7802caeSToomas Soome 
1212d7802caeSToomas Soome 	/* add stage for our target file system slice */
1213d7802caeSToomas Soome 	pl = partlist_alloc();
1214d7802caeSToomas Soome 	if (pl == NULL)
1215d7802caeSToomas Soome 		goto done;
1216d7802caeSToomas Soome 
1217d7802caeSToomas Soome 	device = pl->pl_device;
1218d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1219d7802caeSToomas Soome 	if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1220d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1221d7802caeSToomas Soome 		partlist_free(pl);
1222d7802caeSToomas Soome 		goto done;
12230c946d80SToomas Soome 	}
12240c946d80SToomas Soome 
1225d7802caeSToomas Soome 	device->stage.id = slice;
1226d7802caeSToomas Soome 	device->stage.start = vtoc->efi_parts[slice].p_start;
1227d7802caeSToomas Soome 	device->stage.size = vtoc->efi_parts[slice].p_size;
12280c946d80SToomas Soome 
1229d7802caeSToomas Soome 	/* ZFS and UFS can have stage1 in boot area. */
1230d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS ||
1231d7802caeSToomas Soome 	    data->target.fstype == IB_FS_UFS) {
1232d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1233d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1234d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1235d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1236d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1237d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1238d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1239d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1240d7802caeSToomas Soome 	}
12410c946d80SToomas Soome 
1242d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS) {
1243d7802caeSToomas Soome 		pl = partlist_alloc();
1244d7802caeSToomas Soome 		if (pl == NULL)
1245d7802caeSToomas Soome 			goto done;
12460c946d80SToomas Soome 
1247d7802caeSToomas Soome 		device = pl->pl_device;
1248d7802caeSToomas Soome 		device->stage.devtype = data->device.devtype;
1249d7802caeSToomas Soome 
1250d7802caeSToomas Soome 		if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1251d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1252d7802caeSToomas Soome 			goto done;
12530c946d80SToomas Soome 		}
12540c946d80SToomas Soome 
1255d7802caeSToomas Soome 		device->stage.id = slice;
1256d7802caeSToomas Soome 		device->stage.start = vtoc->efi_parts[slice].p_start;
1257d7802caeSToomas Soome 		device->stage.size = vtoc->efi_parts[slice].p_size;
1258d7802caeSToomas Soome 
1259d7802caeSToomas Soome 		device->stage.offset = BBLK_ZFS_BLK_OFF;
1260d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1261d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1262d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1263d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1264d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1265d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1266d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1267d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1268d7802caeSToomas Soome 	}
1269d7802caeSToomas Soome 	rv = true;
1270d7802caeSToomas Soome done:
1271d7802caeSToomas Soome 	efi_free(vtoc);
1272d7802caeSToomas Soome 	return (rv);
1273d7802caeSToomas Soome }
12740c946d80SToomas Soome 
1275d7802caeSToomas Soome static bool
1276d7802caeSToomas Soome get_start_sector(ib_data_t *data, struct extpartition *v_part,
1277d7802caeSToomas Soome     diskaddr_t *start)
1278d7802caeSToomas Soome {
1279d7802caeSToomas Soome 	struct partlist *pl;
1280d7802caeSToomas Soome 	struct mboot *mbr;
1281d7802caeSToomas Soome 	struct ipart *part;
1282d7802caeSToomas Soome 	struct part_info dkpi;
1283d7802caeSToomas Soome 	struct extpart_info edkpi;
1284d7802caeSToomas Soome 	uint32_t secnum, numsec;
1285d7802caeSToomas Soome 	ext_part_t *epp;
1286d7802caeSToomas Soome 	ushort_t i;
1287d7802caeSToomas Soome 	int fd, rval, pno;
1288d7802caeSToomas Soome 
1289d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1290d7802caeSToomas Soome 		return (false);
1291d7802caeSToomas Soome 
1292d7802caeSToomas Soome 	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
1293d7802caeSToomas Soome 		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
12940c946d80SToomas Soome 			(void) fprintf(stderr, gettext("cannot get the "
12950c946d80SToomas Soome 			    "slice information of the disk\n"));
1296d7802caeSToomas Soome 			(void) close(fd);
1297d7802caeSToomas Soome 			return (false);
12980c946d80SToomas Soome 		} else {
12990c946d80SToomas Soome 			edkpi.p_start = dkpi.p_start;
13000c946d80SToomas Soome 			edkpi.p_length = dkpi.p_length;
13010c946d80SToomas Soome 		}
13020c946d80SToomas Soome 	}
1303d7802caeSToomas Soome 	(void) close(fd);
13040c946d80SToomas Soome 
1305d7802caeSToomas Soome 	/* Set target file system start and size */
1306d7802caeSToomas Soome 	data->target.start = edkpi.p_start;
1307d7802caeSToomas Soome 	data->target.size = edkpi.p_length;
13080c946d80SToomas Soome 
1309d7802caeSToomas Soome 	/* This is our MBR partition start. */
1310d7802caeSToomas Soome 	edkpi.p_start -= v_part->p_start;
13110c946d80SToomas Soome 
1312d7802caeSToomas Soome 	/* Head is always MBR */
1313d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1314d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1315d7802caeSToomas Soome 		return (false);
13160c946d80SToomas Soome 
1317d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1318d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
13190c946d80SToomas Soome 
1320d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1321d7802caeSToomas Soome 		if (part[i].relsect == edkpi.p_start) {
1322d7802caeSToomas Soome 			*start = part[i].relsect;
1323d7802caeSToomas Soome 			return (true);
1324d7802caeSToomas Soome 		}
13250c946d80SToomas Soome 	}
13260c946d80SToomas Soome 
1327d7802caeSToomas Soome 	rval = libfdisk_init(&epp, pl->pl_devname, part, FDISK_READ_DISK);
1328d7802caeSToomas Soome 	if (rval != FDISK_SUCCESS) {
13290c946d80SToomas Soome 		switch (rval) {
13300c946d80SToomas Soome 			/*
13310c946d80SToomas Soome 			 * The first 3 cases are not an error per-se, just that
13320c946d80SToomas Soome 			 * there is no Solaris logical partition
13330c946d80SToomas Soome 			 */
13340c946d80SToomas Soome 			case FDISK_EBADLOGDRIVE:
13350c946d80SToomas Soome 			case FDISK_ENOLOGDRIVE:
13360c946d80SToomas Soome 			case FDISK_EBADMAGIC:
13370c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Solaris "
13380c946d80SToomas Soome 				    "partition not found. "
1339d7802caeSToomas Soome 				    "Aborting operation. %d\n"), rval);
1340d7802caeSToomas Soome 				return (false);
13410c946d80SToomas Soome 			case FDISK_ENOVGEOM:
13420c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13430c946d80SToomas Soome 				    "virtual geometry\n"));
1344d7802caeSToomas Soome 				return (false);
13450c946d80SToomas Soome 			case FDISK_ENOPGEOM:
13460c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13470c946d80SToomas Soome 				    "physical geometry\n"));
1348d7802caeSToomas Soome 				return (false);
13490c946d80SToomas Soome 			case FDISK_ENOLGEOM:
13500c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13510c946d80SToomas Soome 				    "label geometry\n"));
1352d7802caeSToomas Soome 				return (false);
13530c946d80SToomas Soome 			default:
13540c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Failed to "
13550c946d80SToomas Soome 				    "initialize libfdisk.\n"));
1356d7802caeSToomas Soome 				return (false);
13570c946d80SToomas Soome 		}
13580c946d80SToomas Soome 	}
13590c946d80SToomas Soome 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
13600c946d80SToomas Soome 	libfdisk_fini(&epp);
13610c946d80SToomas Soome 	if (rval != FDISK_SUCCESS) {
13620c946d80SToomas Soome 		/* No solaris logical partition */
13630c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Solaris partition not found. "
13640c946d80SToomas Soome 		    "Aborting operation.\n"));
1365d7802caeSToomas Soome 		return (false);
13660c946d80SToomas Soome 	}
1367d7802caeSToomas Soome 	*start = secnum;
1368d7802caeSToomas Soome 	return (true);
1369d7802caeSToomas Soome }
13700c946d80SToomas Soome 
1371d7802caeSToomas Soome /*
1372d7802caeSToomas Soome  * On x86 the VTOC table is inside MBR partition and to get
1373d7802caeSToomas Soome  * absolute sectors, we need to add MBR partition start to VTOC slice start.
1374d7802caeSToomas Soome  */
1375d7802caeSToomas Soome static bool
1376d7802caeSToomas Soome probe_vtoc(ib_data_t *data)
1377d7802caeSToomas Soome {
1378d7802caeSToomas Soome 	struct partlist *pl;
1379d7802caeSToomas Soome 	struct extvtoc exvtoc;
1380d7802caeSToomas Soome 	ib_device_t *device;
1381d7802caeSToomas Soome 	char *path, *ptr;
1382d7802caeSToomas Soome 	ushort_t i;
1383d7802caeSToomas Soome 	int slice, fd;
1384d7802caeSToomas Soome 	diskaddr_t start;
1385d7802caeSToomas Soome 	bool rv;
13860c946d80SToomas Soome 
1387d7802caeSToomas Soome 	rv = false;
1388d7802caeSToomas Soome 
1389d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1390d7802caeSToomas Soome 		return (rv);
1391d7802caeSToomas Soome 
1392d7802caeSToomas Soome 	slice = read_extvtoc(fd, &exvtoc);
1393d7802caeSToomas Soome 	(void) close(fd);
1394d7802caeSToomas Soome 	if (slice < 0)
1395d7802caeSToomas Soome 		return (rv);
1396d7802caeSToomas Soome 	data->device.devtype = IB_DEV_VTOC;
1397d7802caeSToomas Soome 
1398d7802caeSToomas Soome 	if (!get_start_sector(data, exvtoc.v_part + slice, &start))
1399d7802caeSToomas Soome 		return (rv);
1400d7802caeSToomas Soome 
1401d7802caeSToomas Soome 	if (exvtoc.v_part[slice].p_tag == V_BACKUP) {
1402d7802caeSToomas Soome 		/*
1403d7802caeSToomas Soome 		 * NOTE: we could relax there and allow zfs boot on
1404d7802caeSToomas Soome 		 * slice 2, but lets keep traditional limits.
1405d7802caeSToomas Soome 		 */
1406d7802caeSToomas Soome 		(void) fprintf(stderr, gettext(
1407d7802caeSToomas Soome 		    "raw device must be a root slice (not backup)\n"));
1408d7802caeSToomas Soome 		return (rv);
1409d7802caeSToomas Soome 	}
1410d7802caeSToomas Soome 
1411d7802caeSToomas Soome 	if ((path = strdup(data->target.path)) == NULL) {
1412d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1413d7802caeSToomas Soome 		return (false);
1414d7802caeSToomas Soome 	}
1415d7802caeSToomas Soome 
1416d7802caeSToomas Soome 	data->target.start = start + exvtoc.v_part[slice].p_start;
1417d7802caeSToomas Soome 	data->target.size = exvtoc.v_part[slice].p_size;
1418d7802caeSToomas Soome 
1419d7802caeSToomas Soome 	/* Search for boot slice. */
1420d7802caeSToomas Soome 	for (i = 0; i < exvtoc.v_nparts; i++) {
1421d7802caeSToomas Soome 		if (exvtoc.v_part[i].p_tag == V_BOOT)
1422d7802caeSToomas Soome 			break;
1423d7802caeSToomas Soome 	}
1424d7802caeSToomas Soome 
1425d7802caeSToomas Soome 	if (i == exvtoc.v_nparts ||
1426d7802caeSToomas Soome 	    exvtoc.v_part[i].p_size == 0) {
1427d7802caeSToomas Soome 		/* fall back to slice V_BACKUP */
1428d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1429d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_tag == V_BACKUP)
1430d7802caeSToomas Soome 				break;
1431d7802caeSToomas Soome 		}
1432d7802caeSToomas Soome 		/* Still nothing? Error out. */
1433d7802caeSToomas Soome 		if (i == exvtoc.v_nparts ||
1434d7802caeSToomas Soome 		    exvtoc.v_part[i].p_size == 0) {
1435d7802caeSToomas Soome 			free(path);
1436d7802caeSToomas Soome 			return (false);
1437d7802caeSToomas Soome 		}
1438d7802caeSToomas Soome 	}
1439d7802caeSToomas Soome 
1440d7802caeSToomas Soome 	/* Create path. */
1441d7802caeSToomas Soome 	ptr = strrchr(path, 's');
1442d7802caeSToomas Soome 	ptr++;
1443d7802caeSToomas Soome 	*ptr = '\0';
1444d7802caeSToomas Soome 	(void) asprintf(&ptr, "%s%d", path, i);
1445d7802caeSToomas Soome 	free(path);
1446d7802caeSToomas Soome 	if (ptr == NULL) {
1447d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1448d7802caeSToomas Soome 		return (false);
1449d7802caeSToomas Soome 	}
1450d7802caeSToomas Soome 
1451d7802caeSToomas Soome 	pl = partlist_alloc();
1452d7802caeSToomas Soome 	if (pl == NULL) {
1453d7802caeSToomas Soome 		free(ptr);
1454d7802caeSToomas Soome 		return (false);
1455d7802caeSToomas Soome 	}
1456d7802caeSToomas Soome 	pl->pl_devname = ptr;
1457d7802caeSToomas Soome 	device = pl->pl_device;
1458d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1459d7802caeSToomas Soome 	device->stage.id = i;
1460d7802caeSToomas Soome 	device->stage.tag = exvtoc.v_part[i].p_tag;
1461d7802caeSToomas Soome 	device->stage.start = start + exvtoc.v_part[i].p_start;
1462d7802caeSToomas Soome 	device->stage.size = exvtoc.v_part[i].p_size;
1463d7802caeSToomas Soome 
1464d7802caeSToomas Soome 	/* Fix size if this slice is in fact V_BACKUP */
1465d7802caeSToomas Soome 	if (exvtoc.v_part[i].p_tag == V_BACKUP) {
1466d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1467d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start == 0)
1468d7802caeSToomas Soome 				continue;
1469d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_size == 0)
1470d7802caeSToomas Soome 				continue;
1471d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start <
1472d7802caeSToomas Soome 			    device->stage.size)
1473d7802caeSToomas Soome 				device->stage.size =
1474d7802caeSToomas Soome 				    exvtoc.v_part[i].p_start;
14750c946d80SToomas Soome 		}
14760c946d80SToomas Soome 	}
14770c946d80SToomas Soome 
1478d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1479d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1480d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1481d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1482d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1483d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1484d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1485d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1486d7802caeSToomas Soome 
1487d7802caeSToomas Soome 	/* Create instance for stage 2 */
1488d7802caeSToomas Soome 	pl = partlist_alloc();
1489d7802caeSToomas Soome 	if (pl == NULL) {
1490d7802caeSToomas Soome 		free(ptr);
1491d7802caeSToomas Soome 		return (false);
1492d7802caeSToomas Soome 	}
1493d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1494d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1495d7802caeSToomas Soome 		partlist_free(pl);
1496d7802caeSToomas Soome 		return (false);
1497d7802caeSToomas Soome 	}
1498d7802caeSToomas Soome 	pl->pl_device->stage.devtype = data->device.devtype;
1499d7802caeSToomas Soome 	pl->pl_device->stage.id = device->stage.id;
1500d7802caeSToomas Soome 	pl->pl_device->stage.offset = BBLK_BLKLIST_OFF;
1501d7802caeSToomas Soome 	pl->pl_device->stage.tag = device->stage.tag;
1502d7802caeSToomas Soome 	pl->pl_device->stage.start = device->stage.start;
1503d7802caeSToomas Soome 	pl->pl_device->stage.size = device->stage.size;
1504d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1505d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1506d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1507d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1508d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1509d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1510d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1511d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1512d7802caeSToomas Soome 
1513d7802caeSToomas Soome 	/* And we are done. */
1514d7802caeSToomas Soome 	rv = true;
1515d7802caeSToomas Soome 	return (rv);
1516d7802caeSToomas Soome }
1517d7802caeSToomas Soome 
1518d7802caeSToomas Soome static bool
1519d7802caeSToomas Soome probe_mbr(ib_data_t *data)
1520d7802caeSToomas Soome {
1521d7802caeSToomas Soome 	struct partlist *pl;
1522d7802caeSToomas Soome 	struct ipart *part;
1523d7802caeSToomas Soome 	struct mboot *mbr;
1524d7802caeSToomas Soome 	ib_device_t *device;
1525d7802caeSToomas Soome 	char *path, *ptr;
1526d7802caeSToomas Soome 	int i, rv;
1527d7802caeSToomas Soome 
1528d7802caeSToomas Soome 	data->device.devtype = IB_DEV_MBR;
1529d7802caeSToomas Soome 
1530d7802caeSToomas Soome 	/* Head is always MBR */
1531d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1532d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1533d7802caeSToomas Soome 		return (false);
1534d7802caeSToomas Soome 
1535d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1536d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
1537d7802caeSToomas Soome 
1538d7802caeSToomas Soome 	/* Set target file system start and size */
1539d7802caeSToomas Soome 	data->target.start = part[data->target.id - 1].relsect;
1540d7802caeSToomas Soome 	data->target.size = part[data->target.id - 1].numsect;
1541d7802caeSToomas Soome 
1542d7802caeSToomas Soome 	/* Use X86BOOT partition if we have one. */
1543d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1544d7802caeSToomas Soome 		if (part[i].systid == X86BOOT)
1545d7802caeSToomas Soome 			break;
1546d7802caeSToomas Soome 	}
1547d7802caeSToomas Soome 
1548d7802caeSToomas Soome 	/* Keep device name of whole disk device. */
1549d7802caeSToomas Soome 	path = (char *)pl->pl_devname;
1550d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1551d7802caeSToomas Soome 		return (false);
1552d7802caeSToomas Soome 	device = pl->pl_device;
1553d7802caeSToomas Soome 
15540c946d80SToomas Soome 	/*
1555d7802caeSToomas Soome 	 * No X86BOOT, try to use space between MBR and first
1556d7802caeSToomas Soome 	 * partition.
15570c946d80SToomas Soome 	 */
1558d7802caeSToomas Soome 	if (i == FD_NUMPART) {
1559d7802caeSToomas Soome 		/* with pcfs we always write MBR */
1560d7802caeSToomas Soome 		if (data->target.fstype == IB_FS_PCFS) {
1561d7802caeSToomas Soome 			force_mbr = true;
1562d7802caeSToomas Soome 			write_mbr = true;
15630c946d80SToomas Soome 		}
1564d7802caeSToomas Soome 
1565d7802caeSToomas Soome 		pl->pl_devname = strdup(path);
1566d7802caeSToomas Soome 		if (pl->pl_devname == NULL) {
1567d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1568d7802caeSToomas Soome 			partlist_free(pl);
1569d7802caeSToomas Soome 			return (false);
1570d7802caeSToomas Soome 		}
1571d7802caeSToomas Soome 		device->stage.id = 0;
1572d7802caeSToomas Soome 		device->stage.devtype = IB_DEV_MBR;
1573d7802caeSToomas Soome 		device->stage.fstype = IB_FS_NONE;
1574d7802caeSToomas Soome 		device->stage.start = 0;
1575d7802caeSToomas Soome 		device->stage.size = part[0].relsect;
1576d7802caeSToomas Soome 		device->stage.offset = BBLK_BLKLIST_OFF;
1577d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1578d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1579d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1580d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1581d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1582d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1583d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1584d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1585d7802caeSToomas Soome 
1586d7802caeSToomas Soome 		/* We have MBR for stage1 and gap for stage2, we are done. */
1587d7802caeSToomas Soome 		return (true);
15880c946d80SToomas Soome 	}
15890c946d80SToomas Soome 
1590d7802caeSToomas Soome 	if ((path = strdup(path)) == NULL) {
1591d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1592d7802caeSToomas Soome 		partlist_free(pl);
1593d7802caeSToomas Soome 		return (false);
1594d7802caeSToomas Soome 	}
1595d7802caeSToomas Soome 	ptr = strrchr(path, 'p');
1596d7802caeSToomas Soome 	ptr++;
1597d7802caeSToomas Soome 	*ptr = '\0';
1598d7802caeSToomas Soome 	/* partitions are p1..p4 */
1599d7802caeSToomas Soome 	rv = asprintf(&ptr, "%s%d", path, i + 1);
1600d7802caeSToomas Soome 	free(path);
1601d7802caeSToomas Soome 	if (rv < 0) {
1602d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1603d7802caeSToomas Soome 		partlist_free(pl);
1604d7802caeSToomas Soome 		return (false);
1605d7802caeSToomas Soome 	}
1606d7802caeSToomas Soome 	pl->pl_devname = ptr;
1607d7802caeSToomas Soome 	device->stage.id = i + 1;
1608d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1609d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1610d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1611d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1612d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1613d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1614d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1615d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1616d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1617d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1618d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1619d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1620d7802caeSToomas Soome 
1621d7802caeSToomas Soome 	pl = partlist_alloc();
1622d7802caeSToomas Soome 	if (pl == NULL)
1623d7802caeSToomas Soome 		return (false);
1624d7802caeSToomas Soome 	device = pl->pl_device;
1625d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1626d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1627d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1628d7802caeSToomas Soome 		partlist_free(pl);
1629d7802caeSToomas Soome 		return (false);
1630d7802caeSToomas Soome 	}
1631d7802caeSToomas Soome 	device->stage.id = i + 1;
1632d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1633d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1634d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1635d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1636d7802caeSToomas Soome 	device->stage.offset = 1;
1637d7802caeSToomas Soome 	/* This is boot partition */
1638d7802caeSToomas Soome 	device->stage.tag = V_BOOT;
1639d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1640d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1641d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1642d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1643d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1644d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1645d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1646d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1647d7802caeSToomas Soome 
1648d7802caeSToomas Soome 	return (true);
1649d7802caeSToomas Soome }
1650d7802caeSToomas Soome 
1651d7802caeSToomas Soome static bool
1652d7802caeSToomas Soome probe_device(ib_data_t *data, const char *dev)
1653d7802caeSToomas Soome {
1654d7802caeSToomas Soome 	struct partlist *pl;
1655d7802caeSToomas Soome 	struct stat sb;
1656d7802caeSToomas Soome 	const char *ptr;
1657d7802caeSToomas Soome 	char *p0;
1658d7802caeSToomas Soome 	int fd, len;
1659d7802caeSToomas Soome 
1660d7802caeSToomas Soome 	if (dev == NULL)
1661d7802caeSToomas Soome 		return (NULL);
1662d7802caeSToomas Soome 
1663d7802caeSToomas Soome 	len = strlen(dev);
1664d7802caeSToomas Soome 
1665d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1666d7802caeSToomas Soome 		return (false);
1667d7802caeSToomas Soome 
1668d7802caeSToomas Soome 	if (stat(dev, &sb) == -1) {
1669d7802caeSToomas Soome 		perror("stat");
1670d7802caeSToomas Soome 		partlist_free(pl);
1671d7802caeSToomas Soome 		return (false);
1672d7802caeSToomas Soome 	}
1673d7802caeSToomas Soome 
1674d7802caeSToomas Soome 	/* We have regular file, register it and we are done. */
1675d7802caeSToomas Soome 	if (S_ISREG(sb.st_mode) != 0) {
1676d7802caeSToomas Soome 		pl->pl_devname = (char *)dev;
1677d7802caeSToomas Soome 
1678d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_FILE;
1679d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_file_cb;
1680d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1681d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1682d7802caeSToomas Soome 		return (true);
1683d7802caeSToomas Soome 	}
1684d7802caeSToomas Soome 
1685d7802caeSToomas Soome 	/*
1686d7802caeSToomas Soome 	 * This is block device.
1687d7802caeSToomas Soome 	 * We do not allow to specify whole disk device (cXtYdZp0 or cXtYdZ).
1688d7802caeSToomas Soome 	 */
1689d7802caeSToomas Soome 	if ((ptr = strrchr(dev, '/')) == NULL)
1690d7802caeSToomas Soome 		ptr = dev;
1691d7802caeSToomas Soome 	if ((strrchr(ptr, 'p') == NULL && strrchr(ptr, 's') == NULL) ||
1692d7802caeSToomas Soome 	    (dev[len - 2] == 'p' && dev[len - 1] == '0')) {
1693d7802caeSToomas Soome 		(void) fprintf(stderr,
1694d7802caeSToomas Soome 		    gettext("whole disk device is not supported\n"));
1695d7802caeSToomas Soome 		partlist_free(pl);
1696d7802caeSToomas Soome 		return (false);
1697d7802caeSToomas Soome 	}
1698d7802caeSToomas Soome 
1699d7802caeSToomas Soome 	data->target.path = (char *)dev;
1700d7802caeSToomas Soome 	if (!probe_fstyp(data)) {
1701d7802caeSToomas Soome 		partlist_free(pl);
1702d7802caeSToomas Soome 		return (false);
1703d7802caeSToomas Soome 	}
1704d7802caeSToomas Soome 
1705d7802caeSToomas Soome 	/* We start from identifying the whole disk. */
1706d7802caeSToomas Soome 	if ((p0 = strdup(dev)) == NULL) {
1707d7802caeSToomas Soome 		perror("calloc");
1708d7802caeSToomas Soome 		partlist_free(pl);
1709d7802caeSToomas Soome 		return (false);
1710d7802caeSToomas Soome 	}
1711d7802caeSToomas Soome 
1712d7802caeSToomas Soome 	pl->pl_devname = p0;
1713d7802caeSToomas Soome 	/* Change device name to p0 */
1714d7802caeSToomas Soome 	if ((ptr = strrchr(p0, 'p')) == NULL)
1715d7802caeSToomas Soome 		ptr = strrchr(p0, 's');
1716d7802caeSToomas Soome 	p0 = (char *)ptr;
1717d7802caeSToomas Soome 	p0[0] = 'p';
1718d7802caeSToomas Soome 	p0[1] = '0';
1719d7802caeSToomas Soome 	p0[2] = '\0';
1720d7802caeSToomas Soome 
1721d7802caeSToomas Soome 	if ((fd = open_device(pl->pl_devname)) == -1) {
1722d7802caeSToomas Soome 		partlist_free(pl);
1723d7802caeSToomas Soome 		return (false);
1724d7802caeSToomas Soome 	}
1725d7802caeSToomas Soome 
1726d7802caeSToomas Soome 	sector_size = get_media_info(fd);
1727d7802caeSToomas Soome 	(void) close(fd);
1728d7802caeSToomas Soome 
1729d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1730d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_MBR;
1731d7802caeSToomas Soome 	pl->pl_cb.compare = compare_mbr_cb;
1732d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1733d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1734d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1735d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1736d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1737d7802caeSToomas Soome 
1738d7802caeSToomas Soome 	if (probe_gpt(data))
1739d7802caeSToomas Soome 		return (true);
1740d7802caeSToomas Soome 
1741d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1742d7802caeSToomas Soome 		if (probe_vtoc(data))
1743d7802caeSToomas Soome 			return (true);
1744d7802caeSToomas Soome 
1745d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1746d7802caeSToomas Soome 		return (probe_mbr(data));
1747d7802caeSToomas Soome 
1748d7802caeSToomas Soome 	return (false);
1749d7802caeSToomas Soome }
1750d7802caeSToomas Soome 
1751d7802caeSToomas Soome static int
1752d7802caeSToomas Soome read_bootblock_from_file(const char *file, ib_bootblock_t *bblock)
1753d7802caeSToomas Soome {
1754d7802caeSToomas Soome 	struct stat	sb;
1755d7802caeSToomas Soome 	uint32_t	buf_size;
1756d7802caeSToomas Soome 	uint32_t	mboot_off;
1757d7802caeSToomas Soome 	int		fd = -1;
1758d7802caeSToomas Soome 	int		retval = BC_ERROR;
1759d7802caeSToomas Soome 
1760d7802caeSToomas Soome 	assert(bblock != NULL);
1761d7802caeSToomas Soome 	assert(file != NULL);
1762d7802caeSToomas Soome 
1763d7802caeSToomas Soome 	fd = open(file, O_RDONLY);
1764d7802caeSToomas Soome 	if (fd == -1) {
1765d7802caeSToomas Soome 		BOOT_DEBUG("Error opening %s\n", file);
1766d7802caeSToomas Soome 		goto out;
1767d7802caeSToomas Soome 	}
1768d7802caeSToomas Soome 
1769d7802caeSToomas Soome 	if (fstat(fd, &sb) == -1) {
1770d7802caeSToomas Soome 		BOOT_DEBUG("Error getting information (stat) about %s", file);
1771d7802caeSToomas Soome 		perror("stat");
1772d7802caeSToomas Soome 		goto outfd;
1773d7802caeSToomas Soome 	}
1774d7802caeSToomas Soome 
1775d7802caeSToomas Soome 	/* loader bootblock has version built in */
1776d7802caeSToomas Soome 	buf_size = sb.st_size;
1777*46d70dceSToomas Soome 	if (buf_size == 0)
1778*46d70dceSToomas Soome 		goto outfd;
1779d7802caeSToomas Soome 
1780d7802caeSToomas Soome 	bblock->buf_size = buf_size;
1781d7802caeSToomas Soome 	BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
1782d7802caeSToomas Soome 	    bblock->buf_size);
1783d7802caeSToomas Soome 
1784d7802caeSToomas Soome 	bblock->buf = malloc(buf_size);
1785d7802caeSToomas Soome 	if (bblock->buf == NULL) {
1786d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1787d7802caeSToomas Soome 		goto outbuf;
1788d7802caeSToomas Soome 	}
1789d7802caeSToomas Soome 	bblock->file = bblock->buf;
1790d7802caeSToomas Soome 
1791d7802caeSToomas Soome 	if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) {
1792d7802caeSToomas Soome 		BOOT_DEBUG("Read from %s failed\n", file);
1793d7802caeSToomas Soome 		perror("read");
1794d7802caeSToomas Soome 		goto outfd;
1795d7802caeSToomas Soome 	}
1796d7802caeSToomas Soome 
1797d7802caeSToomas Soome 	buf_size = MIN(buf_size, MBOOT_SCAN_SIZE);
1798d7802caeSToomas Soome 	if (find_multiboot(bblock->file, buf_size, &mboot_off)
1799d7802caeSToomas Soome 	    != BC_SUCCESS) {
1800d7802caeSToomas Soome 		(void) fprintf(stderr,
1801d7802caeSToomas Soome 		    gettext("Unable to find multiboot header\n"));
1802d7802caeSToomas Soome 		goto outfd;
1803d7802caeSToomas Soome 	}
1804d7802caeSToomas Soome 
1805d7802caeSToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
1806d7802caeSToomas Soome 	bblock->mboot_off = mboot_off;
1807d7802caeSToomas Soome 
1808d7802caeSToomas Soome 	bblock->file_size =
1809d7802caeSToomas Soome 	    bblock->mboot->load_end_addr - bblock->mboot->load_addr;
1810d7802caeSToomas Soome 	BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
1811d7802caeSToomas Soome 
1812d7802caeSToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
1813d7802caeSToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1814d7802caeSToomas Soome 
1815d7802caeSToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
1816d7802caeSToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
1817d7802caeSToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
1818d7802caeSToomas Soome 
1819d7802caeSToomas Soome 	(void) close(fd);
18200c946d80SToomas Soome 	return (BC_SUCCESS);
1821d7802caeSToomas Soome 
1822d7802caeSToomas Soome outbuf:
1823d7802caeSToomas Soome 	(void) free(bblock->buf);
1824d7802caeSToomas Soome 	bblock->buf = NULL;
1825d7802caeSToomas Soome outfd:
1826d7802caeSToomas Soome 	(void) close(fd);
1827d7802caeSToomas Soome out:
1828d7802caeSToomas Soome 	if (retval == BC_ERROR) {
1829d7802caeSToomas Soome 		(void) fprintf(stderr,
1830d7802caeSToomas Soome 		    gettext("Error reading bootblock from %s\n"),
1831d7802caeSToomas Soome 		    file);
1832d7802caeSToomas Soome 	}
1833d7802caeSToomas Soome 
1834d7802caeSToomas Soome 	if (retval == BC_NOEXTRA) {
1835d7802caeSToomas Soome 		BOOT_DEBUG("No multiboot header found on %s, unable to "
1836d7802caeSToomas Soome 		    "locate extra information area (old/non versioned "
1837d7802caeSToomas Soome 		    "bootblock?) \n", file);
1838d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("No extended information"
1839d7802caeSToomas Soome 		    " found\n"));
1840d7802caeSToomas Soome 	}
1841d7802caeSToomas Soome 	return (retval);
1842d7802caeSToomas Soome }
1843d7802caeSToomas Soome 
1844d7802caeSToomas Soome static void
1845d7802caeSToomas Soome add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
1846d7802caeSToomas Soome {
1847d7802caeSToomas Soome 	bblk_hs_t	hs;
1848d7802caeSToomas Soome 	uint32_t	avail_space;
1849d7802caeSToomas Soome 
1850d7802caeSToomas Soome 	assert(bblock != NULL);
1851d7802caeSToomas Soome 
1852d7802caeSToomas Soome 	if (updt_str == NULL) {
1853d7802caeSToomas Soome 		BOOT_DEBUG("WARNING: no update string passed to "
1854d7802caeSToomas Soome 		    "add_bootblock_einfo()\n");
1855d7802caeSToomas Soome 		return;
1856d7802caeSToomas Soome 	}
1857d7802caeSToomas Soome 
1858d7802caeSToomas Soome 	/* Fill bootblock hashing source information. */
1859d7802caeSToomas Soome 	hs.src_buf = (unsigned char *)bblock->file;
1860d7802caeSToomas Soome 	hs.src_size = bblock->file_size;
1861d7802caeSToomas Soome 	/* How much space for the extended information structure? */
1862d7802caeSToomas Soome 	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1863d7802caeSToomas Soome 	/* Place the extended information structure. */
1864d7802caeSToomas Soome 	add_einfo(bblock->extra, updt_str, &hs, avail_space);
1865d7802caeSToomas Soome }
1866d7802caeSToomas Soome 
1867d7802caeSToomas Soome /*
1868d7802caeSToomas Soome  * set up data for case stage1 is installed as MBR
1869d7802caeSToomas Soome  * set up location and size of bootblock
1870d7802caeSToomas Soome  * set disk guid to provide unique information for biosdev command
1871d7802caeSToomas Soome  */
1872d7802caeSToomas Soome static void
1873d7802caeSToomas Soome prepare_stage1(struct partlist *stage1, struct partlist *stage2, uuid_t uuid)
1874d7802caeSToomas Soome {
1875d7802caeSToomas Soome 	char *src, *dest;
1876d7802caeSToomas Soome 	ib_bootblock_t *bblk;
1877d7802caeSToomas Soome 	ib_device_t *device;
1878d7802caeSToomas Soome 	uint16_t size;
1879d7802caeSToomas Soome 	struct mboot *mbr;
1880d7802caeSToomas Soome 
1881d7802caeSToomas Soome 	src = stage1->pl_stage;
1882d7802caeSToomas Soome 	dest = stage1->pl_src_data;
1883d7802caeSToomas Soome 	device = stage2->pl_device;
1884d7802caeSToomas Soome 
1885d7802caeSToomas Soome 	/* Only copy from valid source. */
1886d7802caeSToomas Soome 	mbr = stage1->pl_stage;
1887d7802caeSToomas Soome 	if (mbr->signature == MBB_MAGIC) {
1888d7802caeSToomas Soome 		/* copy BPB */
1889d7802caeSToomas Soome 		bcopy(src + STAGE1_BPB_OFFSET, dest + STAGE1_BPB_OFFSET,
1890d7802caeSToomas Soome 		    STAGE1_BPB_SIZE);
1891d7802caeSToomas Soome 
1892d7802caeSToomas Soome 		/* copy MBR, note STAGE1_SIG == BOOTSZ */
1893d7802caeSToomas Soome 		bcopy(src + STAGE1_SIG, dest + STAGE1_SIG,
1894d7802caeSToomas Soome 		    SECTOR_SIZE - STAGE1_SIG);
1895d7802caeSToomas Soome 	}
1896d7802caeSToomas Soome 
1897d7802caeSToomas Soome 	bcopy(uuid, dest + STAGE1_STAGE2_UUID, UUID_LEN);
1898d7802caeSToomas Soome 
1899d7802caeSToomas Soome 	/* set stage2 size */
1900d7802caeSToomas Soome 	bblk = stage2->pl_src_data;
1901d7802caeSToomas Soome 	size = bblk->buf_size / SECTOR_SIZE;
1902d7802caeSToomas Soome 	*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = size;
1903d7802caeSToomas Soome 
1904d7802caeSToomas Soome 	/* set stage2 LBA */
1905d7802caeSToomas Soome 	*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
1906d7802caeSToomas Soome 	    device->stage.start + device->stage.offset;
1907d7802caeSToomas Soome 
1908d7802caeSToomas Soome 	/* Copy prepared data to stage1 block read from the disk. */
1909d7802caeSToomas Soome 	bcopy(dest, src, SECTOR_SIZE);
1910d7802caeSToomas Soome }
1911d7802caeSToomas Soome 
1912d7802caeSToomas Soome static void
1913d7802caeSToomas Soome prepare_bootblock(ib_data_t *data, struct partlist *pl, char *updt_str)
1914d7802caeSToomas Soome {
1915d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
1916d7802caeSToomas Soome 	uint64_t		*ptr;
1917d7802caeSToomas Soome 
1918d7802caeSToomas Soome 	assert(pl != NULL);
1919d7802caeSToomas Soome 
1920d7802caeSToomas Soome 	bblock = pl->pl_src_data;
1921*46d70dceSToomas Soome 	if (bblock == NULL)
1922*46d70dceSToomas Soome 		return;
1923d7802caeSToomas Soome 
1924d7802caeSToomas Soome 	ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
1925d7802caeSToomas Soome 	*ptr = data->target.start;
1926d7802caeSToomas Soome 
1927d7802caeSToomas Soome 	/*
1928d7802caeSToomas Soome 	 * the loader bootblock has built in version, if custom
1929d7802caeSToomas Soome 	 * version was provided, update it.
1930d7802caeSToomas Soome 	 */
1931d7802caeSToomas Soome 	if (do_version)
1932d7802caeSToomas Soome 		add_bootblock_einfo(bblock, updt_str);
19330c946d80SToomas Soome }
19340c946d80SToomas Soome 
19350c946d80SToomas Soome static int
1936d7802caeSToomas Soome open_device(const char *path)
19370c946d80SToomas Soome {
19380c946d80SToomas Soome 	struct stat	statbuf = {0};
19390c946d80SToomas Soome 	int		fd = -1;
19400c946d80SToomas Soome 
19410c946d80SToomas Soome 	if (nowrite)
19420c946d80SToomas Soome 		fd = open(path, O_RDONLY);
19430c946d80SToomas Soome 	else
19440c946d80SToomas Soome 		fd = open(path, O_RDWR);
19450c946d80SToomas Soome 
19460c946d80SToomas Soome 	if (fd == -1) {
19470c946d80SToomas Soome 		BOOT_DEBUG("Unable to open %s\n", path);
19480c946d80SToomas Soome 		perror("open");
19490c946d80SToomas Soome 		return (-1);
19500c946d80SToomas Soome 	}
19510c946d80SToomas Soome 
19520c946d80SToomas Soome 	if (fstat(fd, &statbuf) != 0) {
19530c946d80SToomas Soome 		BOOT_DEBUG("Unable to stat %s\n", path);
19540c946d80SToomas Soome 		perror("stat");
19550c946d80SToomas Soome 		(void) close(fd);
19560c946d80SToomas Soome 		return (-1);
19570c946d80SToomas Soome 	}
19580c946d80SToomas Soome 
19590c946d80SToomas Soome 	if (S_ISCHR(statbuf.st_mode) == 0) {
19600c946d80SToomas Soome 		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
19610c946d80SToomas Soome 		    path);
19620c946d80SToomas Soome 		(void) close(fd);
19630c946d80SToomas Soome 		return (-1);
19640c946d80SToomas Soome 	}
19650c946d80SToomas Soome 
19660c946d80SToomas Soome 	return (fd);
19670c946d80SToomas Soome }
19680c946d80SToomas Soome 
1969d7802caeSToomas Soome /*
1970d7802caeSToomas Soome  * We need to record stage2 location and size into pmbr/vbr.
1971d7802caeSToomas Soome  * We need to record target partiton LBA to stage2.
1972d7802caeSToomas Soome  */
1973d7802caeSToomas Soome static void
1974d7802caeSToomas Soome prepare_bblocks(ib_data_t *data)
19750c946d80SToomas Soome {
1976d7802caeSToomas Soome 	struct partlist *pl;
1977d7802caeSToomas Soome 	struct partlist *mbr, *stage1, *stage2;
1978d7802caeSToomas Soome 	uuid_t uuid;
19790c946d80SToomas Soome 
1980d7802caeSToomas Soome 	mbr = stage1 = stage2 = NULL;
1981d7802caeSToomas Soome 	/*
1982d7802caeSToomas Soome 	 * Walk list and pick up BIOS boot blocks. EFI boot programs
1983d7802caeSToomas Soome 	 * can be set in place.
1984d7802caeSToomas Soome 	 */
1985d7802caeSToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
1986d7802caeSToomas Soome 		switch (pl->pl_type) {
1987d7802caeSToomas Soome 		case IB_BBLK_MBR:
1988d7802caeSToomas Soome 			mbr = pl;
1989d7802caeSToomas Soome 			break;
1990d7802caeSToomas Soome 		case IB_BBLK_STAGE1:
1991d7802caeSToomas Soome 			stage1 = pl;
1992d7802caeSToomas Soome 			break;
1993d7802caeSToomas Soome 		case IB_BBLK_STAGE2:
1994d7802caeSToomas Soome 			stage2 = pl;
1995d7802caeSToomas Soome 			/* FALLTHROUGH */
1996d7802caeSToomas Soome 		case IB_BBLK_EFI:
1997d7802caeSToomas Soome 			prepare_bootblock(data, pl, update_str);
1998d7802caeSToomas Soome 			break;
1999d7802caeSToomas Soome 		default:
20000c946d80SToomas Soome 			break;
20010c946d80SToomas Soome 		}
20020c946d80SToomas Soome 	}
20030c946d80SToomas Soome 
2004d7802caeSToomas Soome 	/* If stage2 is missing, we are done. */
2005d7802caeSToomas Soome 	if (stage2 == NULL)
2006d7802caeSToomas Soome 		return;
20070c946d80SToomas Soome 
2008d7802caeSToomas Soome 	/*
2009d7802caeSToomas Soome 	 * Create disk uuid. We only need reasonable amount of uniqueness
2010d7802caeSToomas Soome 	 * to allow biosdev to identify disk based on mbr differences.
2011d7802caeSToomas Soome 	 */
2012d7802caeSToomas Soome 	uuid_generate(uuid);
20130c946d80SToomas Soome 
2014d7802caeSToomas Soome 	if (mbr != NULL) {
2015d7802caeSToomas Soome 		prepare_stage1(mbr, stage2, uuid);
20160c946d80SToomas Soome 
20170c946d80SToomas Soome 		/*
2018d7802caeSToomas Soome 		 * If we have stage1, we point MBR to read stage 1.
20190c946d80SToomas Soome 		 */
2020d7802caeSToomas Soome 		if (stage1 != NULL) {
2021d7802caeSToomas Soome 			char *dest = mbr->pl_stage;
20220c946d80SToomas Soome 
2023d7802caeSToomas Soome 			*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = 1;
2024d7802caeSToomas Soome 			*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
2025d7802caeSToomas Soome 			    stage1->pl_device->stage.start;
20260c946d80SToomas Soome 		}
20270c946d80SToomas Soome 	}
20280c946d80SToomas Soome 
2029d7802caeSToomas Soome 	if (stage1 != NULL) {
2030d7802caeSToomas Soome 		prepare_stage1(stage1, stage2, uuid);
20310c946d80SToomas Soome 	}
20320c946d80SToomas Soome }
20330c946d80SToomas Soome 
20340c946d80SToomas Soome /*
20350c946d80SToomas Soome  * Install a new bootblock on the given device. handle_install() expects argv
20360c946d80SToomas Soome  * to contain 3 parameters (the target device path and the path to the
20370c946d80SToomas Soome  * bootblock.
20380c946d80SToomas Soome  *
20390c946d80SToomas Soome  * Returns:	BC_SUCCESS - if the installation is successful
20400c946d80SToomas Soome  *		BC_ERROR   - if the installation failed
20410c946d80SToomas Soome  *		BC_NOUPDT  - if no installation was performed because the
20420c946d80SToomas Soome  *		             version currently installed is more recent than the
20430c946d80SToomas Soome  *			     supplied one.
20440c946d80SToomas Soome  *
20450c946d80SToomas Soome  */
20460c946d80SToomas Soome static int
2047d7802caeSToomas Soome handle_install(char *progname, int argc, char **argv)
20480c946d80SToomas Soome {
2049d7802caeSToomas Soome 	struct partlist	*pl;
2050d7802caeSToomas Soome 	ib_data_t	data = { 0 };
20510c946d80SToomas Soome 	char		*device_path = NULL;
20520c946d80SToomas Soome 	int		ret = BC_ERROR;
20530c946d80SToomas Soome 
2054d7802caeSToomas Soome 	switch (argc) {
2055d7802caeSToomas Soome 	case 1:
2056d7802caeSToomas Soome 		if ((device_path = strdup(argv[0])) == NULL) {
2057d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2058d7802caeSToomas Soome 			goto done;
2059d7802caeSToomas Soome 		}
2060d7802caeSToomas Soome 		if (asprintf(&stage1, "%s/%s", boot_dir, STAGE1) < 0) {
2061d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2062d7802caeSToomas Soome 			goto done;
2063d7802caeSToomas Soome 		}
2064d7802caeSToomas Soome 		if (asprintf(&stage2, "%s/%s", boot_dir, STAGE2) < 0) {
2065d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2066d7802caeSToomas Soome 			goto done;
2067d7802caeSToomas Soome 		}
2068d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2069d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2070d7802caeSToomas Soome 			goto done;
2071d7802caeSToomas Soome 		}
2072d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2073d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2074d7802caeSToomas Soome 			goto done;
2075d7802caeSToomas Soome 		}
2076d7802caeSToomas Soome 		break;
2077d7802caeSToomas Soome 	case 3:
2078d7802caeSToomas Soome 		if ((stage1 = strdup(argv[0])) == NULL) {
2079d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2080d7802caeSToomas Soome 			goto done;
2081d7802caeSToomas Soome 		}
2082d7802caeSToomas Soome 		if ((stage2 = strdup(argv[1])) == NULL) {
2083d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2084d7802caeSToomas Soome 			goto done;
2085d7802caeSToomas Soome 		}
2086d7802caeSToomas Soome 		if ((device_path = strdup(argv[2])) == NULL) {
2087d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2088d7802caeSToomas Soome 			goto done;
2089d7802caeSToomas Soome 		}
2090d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2091d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2092d7802caeSToomas Soome 			goto done;
2093d7802caeSToomas Soome 		}
2094d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2095d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2096d7802caeSToomas Soome 			goto done;
2097d7802caeSToomas Soome 		}
2098d7802caeSToomas Soome 		break;
2099d7802caeSToomas Soome 	default:
2100d7802caeSToomas Soome 		usage(progname, ret);
2101d7802caeSToomas Soome 	}
21020c946d80SToomas Soome 
2103d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2104d7802caeSToomas Soome 	if (data.plist == NULL) {
2105d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2106d7802caeSToomas Soome 		goto done;
21070c946d80SToomas Soome 	}
2108d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
21090c946d80SToomas Soome 
21100c946d80SToomas Soome 	BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
2111d7802caeSToomas Soome 	    device_path, stage1, stage2);
21120c946d80SToomas Soome 
2113d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2114d7802caeSToomas Soome 		/* Read all data. */
2115d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2116d7802caeSToomas Soome 			if (!pl->pl_cb.read(pl)) {
2117d7802caeSToomas Soome 				printf("\n");
2118d7802caeSToomas Soome 			}
2119d7802caeSToomas Soome 			if (!pl->pl_cb.read_bbl(pl)) {
2120*46d70dceSToomas Soome 				/*
2121*46d70dceSToomas Soome 				 * We will ignore ESP updates in case of
2122*46d70dceSToomas Soome 				 * older system where we are missing
2123*46d70dceSToomas Soome 				 * loader64.efi and loader32.efi.
2124*46d70dceSToomas Soome 				 */
2125*46d70dceSToomas Soome 				if (pl->pl_type != IB_BBLK_EFI)
2126*46d70dceSToomas Soome 					goto cleanup;
2127d7802caeSToomas Soome 			}
2128d7802caeSToomas Soome 		}
21290c946d80SToomas Soome 
2130d7802caeSToomas Soome 		/* Prepare data. */
2131d7802caeSToomas Soome 		prepare_bblocks(&data);
2132d7802caeSToomas Soome 
2133d7802caeSToomas Soome 		/* Commit data to disk. */
2134d7802caeSToomas Soome 		while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) !=
2135d7802caeSToomas Soome 		    NULL) {
2136d7802caeSToomas Soome 			if (pl->pl_cb.compare != NULL &&
2137d7802caeSToomas Soome 			    pl->pl_cb.compare(pl)) {
2138d7802caeSToomas Soome 				if (pl->pl_cb.install != NULL)
2139d7802caeSToomas Soome 					pl->pl_cb.install(&data, pl);
2140d7802caeSToomas Soome 			} else {
2141d7802caeSToomas Soome 				printf("\n");
2142d7802caeSToomas Soome 			}
2143d7802caeSToomas Soome 			STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2144d7802caeSToomas Soome 			partlist_free(pl);
2145d7802caeSToomas Soome 		}
21460c946d80SToomas Soome 	}
2147d7802caeSToomas Soome 	ret = BC_SUCCESS;
21480c946d80SToomas Soome 
2149d7802caeSToomas Soome cleanup:
2150d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2151d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2152d7802caeSToomas Soome 		partlist_free(pl);
21530c946d80SToomas Soome 	}
2154d7802caeSToomas Soome 	free(data.plist);
2155d7802caeSToomas Soome done:
21560c946d80SToomas Soome 	free(stage1);
2157d7802caeSToomas Soome 	free(stage2);
2158d7802caeSToomas Soome 	free(efi32);
2159d7802caeSToomas Soome 	free(efi64);
21600c946d80SToomas Soome 	free(device_path);
21610c946d80SToomas Soome 	return (ret);
21620c946d80SToomas Soome }
21630c946d80SToomas Soome 
21640c946d80SToomas Soome /*
21650c946d80SToomas Soome  * Retrieves from a device the extended information (einfo) associated to the
2166b6dfa2aeSToomas Soome  * file or installed stage2.
2167b6dfa2aeSToomas Soome  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
2168b6dfa2aeSToomas Soome  * or file name.
21690c946d80SToomas Soome  * Returns:
21700c946d80SToomas Soome  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
21710c946d80SToomas Soome  *	  - BC_ERROR (on error)
21720c946d80SToomas Soome  *        - BC_NOEINFO (no extended information available)
21730c946d80SToomas Soome  */
21740c946d80SToomas Soome static int
2175d7802caeSToomas Soome handle_getinfo(char *progname, int argc, char **argv)
21760c946d80SToomas Soome {
2177d7802caeSToomas Soome 	struct partlist	*pl;
2178d7802caeSToomas Soome 	ib_data_t	data = { 0 };
2179d7802caeSToomas Soome 	char		*device_path;
21800c946d80SToomas Soome 
2181d7802caeSToomas Soome 	if (argc != 1) {
21820c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2183bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
21840c946d80SToomas Soome 	}
21850c946d80SToomas Soome 
2186d7802caeSToomas Soome 	if ((device_path = strdup(argv[0])) == NULL) {
2187d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2188d7802caeSToomas Soome 		return (BC_ERROR);
21890c946d80SToomas Soome 	}
21900c946d80SToomas Soome 
2191d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2192d7802caeSToomas Soome 	if (data.plist == NULL) {
2193d7802caeSToomas Soome 		perror("malloc");
2194d7802caeSToomas Soome 		free(device_path);
2195d7802caeSToomas Soome 		return (BC_ERROR);
21960c946d80SToomas Soome 	}
2197d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
21980c946d80SToomas Soome 
2199d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2200d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2201d7802caeSToomas Soome 			if (pl->pl_cb.read(pl))
2202d7802caeSToomas Soome 				pl->pl_cb.print(pl);
2203d7802caeSToomas Soome 			else
2204d7802caeSToomas Soome 				printf("\n");
2205d7802caeSToomas Soome 		}
22060c946d80SToomas Soome 	}
22070c946d80SToomas Soome 
2208d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2209d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2210d7802caeSToomas Soome 		partlist_free(pl);
22110c946d80SToomas Soome 	}
2212d7802caeSToomas Soome 	free(data.plist);
22130c946d80SToomas Soome 
2214d7802caeSToomas Soome 	return (BC_SUCCESS);
22150c946d80SToomas Soome }
22160c946d80SToomas Soome 
22170c946d80SToomas Soome /*
22180c946d80SToomas Soome  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
22190c946d80SToomas Soome  *
22200c946d80SToomas Soome  * Returns:
22210c946d80SToomas Soome  *	- BC_SUCCESS (a successful propagation happened)
22220c946d80SToomas Soome  *	- BC_ERROR (an error occurred)
22230c946d80SToomas Soome  *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
22240c946d80SToomas Soome  *			there is no multiboot information)
22250c946d80SToomas Soome  */
22260c946d80SToomas Soome static int
2227d7802caeSToomas Soome handle_mirror(char *progname, int argc, char **argv)
22280c946d80SToomas Soome {
2229d7802caeSToomas Soome 	ib_data_t src = { 0 };
2230d7802caeSToomas Soome 	ib_data_t dest = { 0 };
2231d7802caeSToomas Soome 	struct partlist *pl_src, *pl_dest;
2232d7802caeSToomas Soome 	char		*curr_device_path = NULL;
2233d7802caeSToomas Soome 	char		*attach_device_path = NULL;
22340c946d80SToomas Soome 	int		retval = BC_ERROR;
22350c946d80SToomas Soome 
2236d7802caeSToomas Soome 	if (argc == 2) {
2237d7802caeSToomas Soome 		curr_device_path = strdup(argv[0]);
2238d7802caeSToomas Soome 		attach_device_path = strdup(argv[1]);
2239d7802caeSToomas Soome 	}
22400c946d80SToomas Soome 
22410c946d80SToomas Soome 	if (!curr_device_path || !attach_device_path) {
2242d7802caeSToomas Soome 		free(curr_device_path);
2243d7802caeSToomas Soome 		free(attach_device_path);
22440c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2245bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22460c946d80SToomas Soome 	}
22470c946d80SToomas Soome 	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
22480c946d80SToomas Soome 	    " %s\n", curr_device_path, attach_device_path);
22490c946d80SToomas Soome 
2250d7802caeSToomas Soome 	src.plist = malloc(sizeof (*src.plist));
2251d7802caeSToomas Soome 	if (src.plist == NULL) {
2252d7802caeSToomas Soome 		perror("malloc");
2253d7802caeSToomas Soome 		return (BC_ERROR);
2254d7802caeSToomas Soome 	}
2255d7802caeSToomas Soome 	STAILQ_INIT(src.plist);
2256d7802caeSToomas Soome 
2257d7802caeSToomas Soome 	dest.plist = malloc(sizeof (*dest.plist));
2258d7802caeSToomas Soome 	if (dest.plist == NULL) {
2259d7802caeSToomas Soome 		perror("malloc");
2260d7802caeSToomas Soome 		goto out;
2261d7802caeSToomas Soome 	}
2262d7802caeSToomas Soome 	STAILQ_INIT(dest.plist);
22630c946d80SToomas Soome 
2264d7802caeSToomas Soome 	if (!probe_device(&src, curr_device_path)) {
22650c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22660c946d80SToomas Soome 		    "information from %s (current device)\n"),
22670c946d80SToomas Soome 		    curr_device_path);
2268d7802caeSToomas Soome 		goto out;
22690c946d80SToomas Soome 	}
22700c946d80SToomas Soome 
2271d7802caeSToomas Soome 	if (!probe_device(&dest, attach_device_path) != BC_SUCCESS) {
22720c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22730c946d80SToomas Soome 		    "information from %s (attaching device)\n"),
22740c946d80SToomas Soome 		    attach_device_path);
2275d7802caeSToomas Soome 		goto cleanup_src;
22760c946d80SToomas Soome 	}
22770c946d80SToomas Soome 
2278d7802caeSToomas Soome 	write_mbr = true;
2279d7802caeSToomas Soome 	force_mbr = true;
2280d7802caeSToomas Soome 
2281d7802caeSToomas Soome 	pl_dest = STAILQ_FIRST(dest.plist);
2282d7802caeSToomas Soome 	STAILQ_FOREACH(pl_src, src.plist, pl_next) {
2283d7802caeSToomas Soome 		if (pl_dest == NULL) {
2284d7802caeSToomas Soome 			(void) fprintf(stderr,
2285d7802caeSToomas Soome 			    gettext("Destination disk layout is different "
2286d7802caeSToomas Soome 			    "from source, can not mirror.\n"));
2287d7802caeSToomas Soome 			goto cleanup;
2288d7802caeSToomas Soome 		}
2289d7802caeSToomas Soome 		if (!pl_src->pl_cb.read(pl_src)) {
2290d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2291d7802caeSToomas Soome 			    "boot block from %s\n"), pl_src->pl_devname);
2292d7802caeSToomas Soome 			goto cleanup;
2293d7802caeSToomas Soome 		}
2294d7802caeSToomas Soome 		if (!pl_dest->pl_cb.read(pl_dest)) {
2295d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2296d7802caeSToomas Soome 			    "boot block from %s\n"), pl_dest->pl_devname);
2297d7802caeSToomas Soome 		}
2298d7802caeSToomas Soome 
2299d7802caeSToomas Soome 		/* Set source pl_stage to destination source data */
2300d7802caeSToomas Soome 		pl_dest->pl_src_data = pl_src->pl_stage;
2301d7802caeSToomas Soome 		pl_src->pl_stage = NULL;
23020c946d80SToomas Soome 
2303d7802caeSToomas Soome 		pl_dest = STAILQ_NEXT(pl_dest, pl_next);
23040c946d80SToomas Soome 	}
23050c946d80SToomas Soome 
2306d7802caeSToomas Soome 	/* Prepare data. */
2307d7802caeSToomas Soome 	prepare_bblocks(&dest);
23080c946d80SToomas Soome 
2309d7802caeSToomas Soome 	/* Commit data to disk. */
2310d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2311d7802caeSToomas Soome 		pl_dest->pl_cb.install(&dest, pl_dest);
2312d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2313d7802caeSToomas Soome 		partlist_free(pl_dest);
2314d7802caeSToomas Soome 
2315d7802caeSToomas Soome 		/* Free source list */
2316d7802caeSToomas Soome 		pl_src = STAILQ_LAST(src.plist, partlist, pl_next);
2317d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2318d7802caeSToomas Soome 		partlist_free(pl_src);
2319d7802caeSToomas Soome 	}
2320d7802caeSToomas Soome 	retval = BC_SUCCESS;
2321d7802caeSToomas Soome 
2322d7802caeSToomas Soome cleanup:
2323d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2324d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2325d7802caeSToomas Soome 		partlist_free(pl_dest);
2326d7802caeSToomas Soome 	}
2327d7802caeSToomas Soome 	free(dest.plist);
2328d7802caeSToomas Soome cleanup_src:
2329d7802caeSToomas Soome 	while ((pl_src = STAILQ_LAST(src.plist, partlist, pl_next)) != NULL) {
2330d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2331d7802caeSToomas Soome 		partlist_free(pl_src);
2332d7802caeSToomas Soome 	}
2333d7802caeSToomas Soome 	free(src.plist);
2334d7802caeSToomas Soome out:
23350c946d80SToomas Soome 	free(curr_device_path);
23360c946d80SToomas Soome 	free(attach_device_path);
23370c946d80SToomas Soome 	return (retval);
23380c946d80SToomas Soome }
23390c946d80SToomas Soome 
2340d7802caeSToomas Soome #define	USAGE_STRING	\
2341d7802caeSToomas Soome "Usage:\t%s [-fFmn] [-b boot_dir] [-u verstr]\n"	\
2342d7802caeSToomas Soome "\t\t[stage1 stage2] raw-device\n"			\
2343d7802caeSToomas Soome "\t%s -M [-n] raw-device attach-raw-device\n"		\
2344d7802caeSToomas Soome "\t%s [-e|-V] -i raw-device | file\n"
23450c946d80SToomas Soome 
23460c946d80SToomas Soome #define	CANON_USAGE_STR	gettext(USAGE_STRING)
23470c946d80SToomas Soome 
23480c946d80SToomas Soome static void
2349bdecfb1eSToomas Soome usage(char *progname, int rc)
23500c946d80SToomas Soome {
23510c946d80SToomas Soome 	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
2352d7802caeSToomas Soome 	fini_yes();
2353bdecfb1eSToomas Soome 	exit(rc);
23540c946d80SToomas Soome }
23550c946d80SToomas Soome 
23560c946d80SToomas Soome int
23570c946d80SToomas Soome main(int argc, char **argv)
23580c946d80SToomas Soome {
23590c946d80SToomas Soome 	int	opt;
23600c946d80SToomas Soome 	int	ret;
23610c946d80SToomas Soome 	char	*progname;
2362d7802caeSToomas Soome 	struct stat sb;
23630c946d80SToomas Soome 
23640c946d80SToomas Soome 	(void) setlocale(LC_ALL, "");
23650c946d80SToomas Soome 	(void) textdomain(TEXT_DOMAIN);
2366bdecfb1eSToomas Soome 	if (init_yes() < 0)
2367bdecfb1eSToomas Soome 		errx(BC_ERROR, gettext(ERR_MSG_INIT_YES), strerror(errno));
2368bdecfb1eSToomas Soome 
2369d7802caeSToomas Soome 	/* Needed for mount pcfs. */
2370d7802caeSToomas Soome 	tzset();
2371d7802caeSToomas Soome 
2372bdecfb1eSToomas Soome 	/* Determine our name */
2373bdecfb1eSToomas Soome 	progname = basename(argv[0]);
23740c946d80SToomas Soome 
2375d7802caeSToomas Soome 	while ((opt = getopt(argc, argv, "b:deFfhiMmnu:V")) != EOF) {
23760c946d80SToomas Soome 		switch (opt) {
2377d7802caeSToomas Soome 		case 'b':
2378d7802caeSToomas Soome 			boot_dir = strdup(optarg);
2379d7802caeSToomas Soome 			if (boot_dir == NULL) {
2380d7802caeSToomas Soome 				err(BC_ERROR,
2381d7802caeSToomas Soome 				    gettext("Memory allocation failure"));
2382d7802caeSToomas Soome 			}
2383d7802caeSToomas Soome 			if (lstat(boot_dir, &sb) != 0) {
2384d7802caeSToomas Soome 				err(BC_ERROR, boot_dir);
2385d7802caeSToomas Soome 			}
2386d7802caeSToomas Soome 			if (!S_ISDIR(sb.st_mode)) {
2387d7802caeSToomas Soome 				errx(BC_ERROR, gettext("%s: not a directory"),
2388d7802caeSToomas Soome 				    boot_dir);
2389d7802caeSToomas Soome 			}
2390d7802caeSToomas Soome 			break;
23910c946d80SToomas Soome 		case 'd':
2392d7802caeSToomas Soome 			boot_debug = true;
23930c946d80SToomas Soome 			break;
23940c946d80SToomas Soome 		case 'e':
2395d7802caeSToomas Soome 			strip = true;
23960c946d80SToomas Soome 			break;
23970c946d80SToomas Soome 		case 'F':
2398d7802caeSToomas Soome 			force_update = true;
23990c946d80SToomas Soome 			break;
24000c946d80SToomas Soome 		case 'f':
2401d7802caeSToomas Soome 			force_mbr = true;
24020c946d80SToomas Soome 			break;
24030c946d80SToomas Soome 		case 'h':
2404bdecfb1eSToomas Soome 			usage(progname, BC_SUCCESS);
24050c946d80SToomas Soome 			break;
24060c946d80SToomas Soome 		case 'i':
2407d7802caeSToomas Soome 			do_getinfo = true;
24080c946d80SToomas Soome 			break;
24090c946d80SToomas Soome 		case 'M':
2410d7802caeSToomas Soome 			do_mirror_bblk = true;
24110c946d80SToomas Soome 			break;
24120c946d80SToomas Soome 		case 'm':
2413d7802caeSToomas Soome 			write_mbr = true;
24140c946d80SToomas Soome 			break;
24150c946d80SToomas Soome 		case 'n':
2416d7802caeSToomas Soome 			nowrite = true;
24170c946d80SToomas Soome 			break;
24180c946d80SToomas Soome 		case 'u':
2419d7802caeSToomas Soome 			do_version = true;
24200c946d80SToomas Soome 
2421bdecfb1eSToomas Soome 			update_str = strdup(optarg);
24220c946d80SToomas Soome 			if (update_str == NULL) {
24230c946d80SToomas Soome 				perror(gettext("Memory allocation failure"));
24240c946d80SToomas Soome 				exit(BC_ERROR);
24250c946d80SToomas Soome 			}
24260c946d80SToomas Soome 			break;
24270c946d80SToomas Soome 		case 'V':
2428d7802caeSToomas Soome 			verbose_dump = true;
24290c946d80SToomas Soome 			break;
24300c946d80SToomas Soome 		default:
24310c946d80SToomas Soome 			/* fall through to process non-optional args */
24320c946d80SToomas Soome 			break;
24330c946d80SToomas Soome 		}
24340c946d80SToomas Soome 	}
24350c946d80SToomas Soome 
24360c946d80SToomas Soome 	/* check arguments */
24370c946d80SToomas Soome 	check_options(progname);
24380c946d80SToomas Soome 
24390c946d80SToomas Soome 	if (nowrite)
24400c946d80SToomas Soome 		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
24410c946d80SToomas Soome 		    " be written to disk.\n"));
24420c946d80SToomas Soome 
24430c946d80SToomas Soome 	if (do_getinfo) {
2444d7802caeSToomas Soome 		ret = handle_getinfo(progname, argc - optind, argv + optind);
24450c946d80SToomas Soome 	} else if (do_mirror_bblk) {
2446d7802caeSToomas Soome 		ret = handle_mirror(progname, argc - optind, argv + optind);
24470c946d80SToomas Soome 	} else {
2448d7802caeSToomas Soome 		ret = handle_install(progname, argc - optind, argv + optind);
24490c946d80SToomas Soome 	}
2450d7802caeSToomas Soome 	fini_yes();
24510c946d80SToomas Soome 	return (ret);
24520c946d80SToomas Soome }
24530c946d80SToomas Soome 
24540c946d80SToomas Soome #define	MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
24550c946d80SToomas Soome static void
24560c946d80SToomas Soome check_options(char *progname)
24570c946d80SToomas Soome {
24580c946d80SToomas Soome 	if (do_getinfo && do_mirror_bblk) {
24590c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
24600c946d80SToomas Soome 		    "specified at the same time\n"));
2461bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
24620c946d80SToomas Soome 	}
24630c946d80SToomas Soome 
24640c946d80SToomas Soome 	if (do_mirror_bblk) {
24650c946d80SToomas Soome 		/*
24660c946d80SToomas Soome 		 * -u and -F may actually reflect a user intent that is not
24670c946d80SToomas Soome 		 * correct with this command (mirror can be interpreted
24680c946d80SToomas Soome 		 * "similar" to install. Emit a message and continue.
24690c946d80SToomas Soome 		 * -e and -V have no meaning, be quiet here and only report the
24700c946d80SToomas Soome 		 * incongruence if a debug output is requested.
24710c946d80SToomas Soome 		 */
24720c946d80SToomas Soome 		if (do_version) {
24730c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-u");
2474d7802caeSToomas Soome 			do_version = false;
24750c946d80SToomas Soome 		}
24760c946d80SToomas Soome 		if (force_update) {
24770c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-F");
2478d7802caeSToomas Soome 			force_update = false;
24790c946d80SToomas Soome 		}
24800c946d80SToomas Soome 		if (strip || verbose_dump) {
24810c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
2482d7802caeSToomas Soome 			strip = false;
2483d7802caeSToomas Soome 			verbose_dump = false;
24840c946d80SToomas Soome 		}
24850c946d80SToomas Soome 	}
24860c946d80SToomas Soome 
2487d7802caeSToomas Soome 	if ((strip || verbose_dump) && !do_getinfo)
2488d7802caeSToomas Soome 		usage(progname, BC_ERROR);
2489d7802caeSToomas Soome 
24900c946d80SToomas Soome 	if (do_getinfo) {
24910c946d80SToomas Soome 		if (write_mbr || force_mbr || do_version || force_update) {
24920c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
2493d7802caeSToomas Soome 			write_mbr = force_mbr = do_version = false;
2494d7802caeSToomas Soome 			force_update = false;
24950c946d80SToomas Soome 		}
24960c946d80SToomas Soome 	}
24970c946d80SToomas Soome }
2498