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;
1099937ff19SToomas Soome static bool	write_vbr = false;
110d7802caeSToomas Soome static bool	force_mbr = false;
111d7802caeSToomas Soome static bool	force_update = false;
112d7802caeSToomas Soome static bool	do_getinfo = false;
113d7802caeSToomas Soome static bool	do_version = false;
114d7802caeSToomas Soome static bool	do_mirror_bblk = false;
115d7802caeSToomas Soome static bool	strip = false;
116d7802caeSToomas Soome static bool	verbose_dump = false;
117d7802caeSToomas Soome static size_t	sector_size = SECTOR_SIZE;
1180c946d80SToomas Soome 
1190c946d80SToomas Soome /* Versioning string, if present. */
1200c946d80SToomas Soome static char		*update_str;
1210c946d80SToomas Soome 
122d7802caeSToomas Soome /* Default location of boot programs. */
123d7802caeSToomas Soome static char		*boot_dir = "/boot";
124d7802caeSToomas Soome 
125d7802caeSToomas Soome /* Our boot programs */
126d7802caeSToomas Soome #define	STAGE1		"pmbr"
127d7802caeSToomas Soome #define	STAGE2		"gptzfsboot"
128d7802caeSToomas Soome #define	BOOTIA32	"bootia32.efi"
129d7802caeSToomas Soome #define	BOOTX64		"bootx64.efi"
130d7802caeSToomas Soome #define	LOADER32	"loader32.efi"
131d7802caeSToomas Soome #define	LOADER64	"loader64.efi"
132d7802caeSToomas Soome 
133d7802caeSToomas Soome static char *stage1;
134d7802caeSToomas Soome static char *stage2;
135d7802caeSToomas Soome static char *efi32;
136d7802caeSToomas Soome static char *efi64;
137d7802caeSToomas Soome 
138d7802caeSToomas Soome #define	GRUB_VERSION_OFF (0x3e)
139d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MAJOR 3
140d7802caeSToomas Soome #define	GRUB_COMPAT_VERSION_MINOR 2
141d7802caeSToomas Soome #define	GRUB_VERSION (2 << 8 | 3) /* 3.2 */
142d7802caeSToomas Soome 
143d7802caeSToomas Soome #define	LOADER_VERSION (1)
144d7802caeSToomas Soome #define	LOADER_JOYENT_VERSION (2)
145d7802caeSToomas Soome 
146d7802caeSToomas Soome typedef enum {
147d7802caeSToomas Soome 	MBR_TYPE_UNKNOWN,
148d7802caeSToomas Soome 	MBR_TYPE_GRUB1,
149d7802caeSToomas Soome 	MBR_TYPE_LOADER,
150d7802caeSToomas Soome 	MBR_TYPE_LOADER_JOYENT,
151d7802caeSToomas Soome } mbr_type_t;
152d7802caeSToomas Soome 
1530c946d80SToomas Soome /*
1540c946d80SToomas Soome  * Temporary buffer to store the first 32K of data looking for a multiboot
1550c946d80SToomas Soome  * signature.
1560c946d80SToomas Soome  */
1570c946d80SToomas Soome char			mboot_scan[MBOOT_SCAN_SIZE];
1580c946d80SToomas Soome 
1590c946d80SToomas Soome /* Function prototypes. */
1600c946d80SToomas Soome static void check_options(char *);
161d7802caeSToomas Soome static int open_device(const char *);
162d7802caeSToomas Soome static char *make_blkdev(const char *);
1630c946d80SToomas Soome 
164d7802caeSToomas Soome static int read_bootblock_from_file(const char *, ib_bootblock_t *);
1650c946d80SToomas Soome static void add_bootblock_einfo(ib_bootblock_t *, char *);
166d7802caeSToomas Soome static void prepare_bootblock(ib_data_t *, struct partlist *, char *);
167d7802caeSToomas Soome static int handle_install(char *, int, char **);
168d7802caeSToomas Soome static int handle_getinfo(char *, int, char **);
169d7802caeSToomas Soome static int handle_mirror(char *, int, char **);
170bdecfb1eSToomas Soome static void usage(char *, int) __NORETURN;
1710c946d80SToomas Soome 
172d7802caeSToomas Soome static char *
stagefs_mount(char * blkdev,struct partlist * plist)173d7802caeSToomas Soome stagefs_mount(char *blkdev, struct partlist *plist)
1740c946d80SToomas Soome {
175d7802caeSToomas Soome 	char *path;
176d7802caeSToomas Soome 	char optbuf[MAX_MNTOPT_STR] = { '\0', };
177d7802caeSToomas Soome 	char *template = strdup("/tmp/ibootXXXXXX");
178d7802caeSToomas Soome 	int ret;
1790c946d80SToomas Soome 
180d7802caeSToomas Soome 	if (template == NULL)
181d7802caeSToomas Soome 		return (NULL);
1820c946d80SToomas Soome 
183d7802caeSToomas Soome 	if ((path = mkdtemp(template)) == NULL) {
184d7802caeSToomas Soome 		free(template);
185d7802caeSToomas Soome 		return (NULL);
1860c946d80SToomas Soome 	}
187d7802caeSToomas Soome 
188d7802caeSToomas Soome 	(void) snprintf(optbuf, MAX_MNTOPT_STR, "timezone=%d",
189d7802caeSToomas Soome 	    timezone);
190d7802caeSToomas Soome 	ret = mount(blkdev, path, MS_OPTIONSTR,
191d7802caeSToomas Soome 	    MNTTYPE_PCFS, NULL, 0, optbuf, MAX_MNTOPT_STR);
192d7802caeSToomas Soome 	if (ret != 0) {
193d7802caeSToomas Soome 		(void) rmdir(path);
194d7802caeSToomas Soome 		free(path);
195d7802caeSToomas Soome 		path = NULL;
196d7802caeSToomas Soome 	}
197d7802caeSToomas Soome 	plist->pl_device->stage.mntpnt = path;
198d7802caeSToomas Soome 	return (path);
1990c946d80SToomas Soome }
2000c946d80SToomas Soome 
201d7802caeSToomas Soome static void
install_stage1_cb(void * data,struct partlist * plist)202d7802caeSToomas Soome install_stage1_cb(void *data, struct partlist *plist)
2030c946d80SToomas Soome {
204d7802caeSToomas Soome 	int rv, fd;
205d7802caeSToomas Soome 	ib_device_t *device = plist->pl_device;
2060c946d80SToomas Soome 
207d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR && !write_mbr)
208d7802caeSToomas Soome 		return;
2090c946d80SToomas Soome 
210d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
211d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
212d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
2130c946d80SToomas Soome 		perror("open");
214d7802caeSToomas Soome 		return;
2150c946d80SToomas Soome 	}
2160c946d80SToomas Soome 
217d7802caeSToomas Soome 	rv = write_out(fd, plist->pl_stage, sector_size, 0);
218d7802caeSToomas Soome 	if (rv != BC_SUCCESS) {
219d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot write "
220d7802caeSToomas Soome 		    "partition boot sector\n"));
221d7802caeSToomas Soome 		perror("write");
222d7802caeSToomas Soome 	} else {
223d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("stage1 written to "
2249937ff19SToomas Soome 		    "%s %d sector 0 (abs %d)\n\n"),
225d7802caeSToomas Soome 		    device->devtype == IB_DEV_MBR? "partition" : "slice",
226d7802caeSToomas Soome 		    device->stage.id, device->stage.start);
2270c946d80SToomas Soome 	}
228d7802caeSToomas Soome }
2290c946d80SToomas Soome 
230d7802caeSToomas Soome static void
install_stage2_cb(void * data,struct partlist * plist)231d7802caeSToomas Soome install_stage2_cb(void *data, struct partlist *plist)
232d7802caeSToomas Soome {
233d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
234d7802caeSToomas Soome 	int fd, ret;
235d7802caeSToomas Soome 	off_t offset;
236d7802caeSToomas Soome 	uint64_t abs;
2370c946d80SToomas Soome 
238d7802caeSToomas Soome 	/*
239d7802caeSToomas Soome 	 * ZFS bootblock area is 3.5MB, make sure we can fit.
240d7802caeSToomas Soome 	 * buf_size is size of bootblk+EINFO.
241d7802caeSToomas Soome 	 */
242d7802caeSToomas Soome 	if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
243d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("bootblock is too large\n"));
244d7802caeSToomas Soome 		return;
245d7802caeSToomas Soome 	}
2460c946d80SToomas Soome 
247d7802caeSToomas Soome 	abs = plist->pl_device->stage.start + plist->pl_device->stage.offset;
248d7802caeSToomas Soome 
249d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
250d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("cannot open "
251d7802caeSToomas Soome 		    "device %s\n"), plist->pl_devname);
252d7802caeSToomas Soome 		perror("open");
253d7802caeSToomas Soome 		return;
254d7802caeSToomas Soome 	}
255d7802caeSToomas Soome 	offset = plist->pl_device->stage.offset * SECTOR_SIZE;
256d7802caeSToomas Soome 	ret = write_out(fd, bblock->buf, bblock->buf_size, offset);
257d7802caeSToomas Soome 	(void) close(fd);
258d7802caeSToomas Soome 	if (ret != BC_SUCCESS) {
259d7802caeSToomas Soome 		BOOT_DEBUG("Error writing the ZFS bootblock "
260d7802caeSToomas Soome 		    "to %s at offset %d\n", plist->pl_devname, offset);
261d7802caeSToomas Soome 		return;
262d7802caeSToomas Soome 	}
263d7802caeSToomas Soome 	(void) fprintf(stdout, gettext("bootblock written for %s,"
2649937ff19SToomas Soome 	    " %d sectors starting at %d (abs %lld)\n\n"), plist->pl_devname,
265d7802caeSToomas Soome 	    (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
266d7802caeSToomas Soome }
267d7802caeSToomas Soome 
268d7802caeSToomas Soome static bool
mkfs_pcfs(const char * dev)269d7802caeSToomas Soome mkfs_pcfs(const char *dev)
270d7802caeSToomas Soome {
271d7802caeSToomas Soome 	pid_t pid, w;
272d7802caeSToomas Soome 	posix_spawnattr_t attr;
273d7802caeSToomas Soome 	posix_spawn_file_actions_t file_actions;
274d7802caeSToomas Soome 	int status;
275d7802caeSToomas Soome 	char *cmd[7];
276d7802caeSToomas Soome 
277d7802caeSToomas Soome 	if (posix_spawnattr_init(&attr))
278d7802caeSToomas Soome 		return (false);
279d7802caeSToomas Soome 	if (posix_spawn_file_actions_init(&file_actions)) {
280d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
281d7802caeSToomas Soome 		return (false);
282d7802caeSToomas Soome 	}
283d7802caeSToomas Soome 
284d7802caeSToomas Soome 	if (posix_spawnattr_setflags(&attr,
285d7802caeSToomas Soome 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP)) {
286d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
287d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
288d7802caeSToomas Soome 		return (false);
289d7802caeSToomas Soome 	}
290d7802caeSToomas Soome 	if (posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null",
291d7802caeSToomas Soome 	    O_RDONLY, 0)) {
292d7802caeSToomas Soome 		(void) posix_spawnattr_destroy(&attr);
293d7802caeSToomas Soome 		(void) posix_spawn_file_actions_destroy(&file_actions);
294d7802caeSToomas Soome 		return (false);
295d7802caeSToomas Soome 	}
296d7802caeSToomas Soome 
297d7802caeSToomas Soome 	cmd[0] = "/usr/sbin/mkfs";
298d7802caeSToomas Soome 	cmd[1] = "-F";
299d7802caeSToomas Soome 	cmd[2] = "pcfs";
300d7802caeSToomas Soome 	cmd[3] = "-o";
301d7802caeSToomas Soome 	cmd[4] = "fat=32";
302d7802caeSToomas Soome 	cmd[5] = (char *)dev;
303d7802caeSToomas Soome 	cmd[6] = NULL;
304d7802caeSToomas Soome 
305d7802caeSToomas Soome 	if (posix_spawn(&pid, cmd[0], &file_actions, &attr, cmd, NULL))
306d7802caeSToomas Soome 		return (false);
307d7802caeSToomas Soome 	(void) posix_spawnattr_destroy(&attr);
308d7802caeSToomas Soome 	(void) posix_spawn_file_actions_destroy(&file_actions);
309d7802caeSToomas Soome 
310d7802caeSToomas Soome 	do {
311d7802caeSToomas Soome 		w = waitpid(pid, &status, 0);
312d7802caeSToomas Soome 	} while (w == -1 && errno == EINTR);
313d7802caeSToomas Soome 	if (w == -1)
314d7802caeSToomas Soome 		status = -1;
315d7802caeSToomas Soome 
316d7802caeSToomas Soome 	return (status != -1);
317d7802caeSToomas Soome }
318d7802caeSToomas Soome 
319d7802caeSToomas Soome static void
install_esp_cb(void * data,struct partlist * plist)320d7802caeSToomas Soome install_esp_cb(void *data, struct partlist *plist)
321d7802caeSToomas Soome {
322d7802caeSToomas Soome 	fstyp_handle_t fhdl;
323d7802caeSToomas Soome 	const char *fident;
324d7802caeSToomas Soome 	bool pcfs;
325d7802caeSToomas Soome 	char *blkdev, *path, *file;
326d7802caeSToomas Soome 	FILE *fp;
327d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
328d7802caeSToomas Soome 	ib_bootblock_t *bblock = plist->pl_src_data;
329d7802caeSToomas Soome 	int fd, ret;
330d7802caeSToomas Soome 
331d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
332d7802caeSToomas Soome 		return;
333d7802caeSToomas Soome 
334d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
335d7802caeSToomas Soome 		(void) close(fd);
336d7802caeSToomas Soome 		return;
337d7802caeSToomas Soome 	}
338d7802caeSToomas Soome 
339d7802caeSToomas Soome 	pcfs = false;
340d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) == 0) {
341d7802caeSToomas Soome 		if (strcmp(fident, MNTTYPE_PCFS) == 0)
342d7802caeSToomas Soome 			pcfs = true;
343d7802caeSToomas Soome 	}
344d7802caeSToomas Soome 	fstyp_fini(fhdl);
345d7802caeSToomas Soome 	(void) close(fd);
346d7802caeSToomas Soome 
347d7802caeSToomas Soome 	if (!pcfs) {
348d7802caeSToomas Soome 		(void) printf(gettext("Creating pcfs on ESP %s\n"),
349d7802caeSToomas Soome 		    plist->pl_devname);
350d7802caeSToomas Soome 
351d7802caeSToomas Soome 		if (!mkfs_pcfs(plist->pl_devname)) {
352d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("mkfs -F pcfs failed "
353d7802caeSToomas Soome 			    "on %s\n"), plist->pl_devname);
354d7802caeSToomas Soome 			return;
355d7802caeSToomas Soome 		}
356d7802caeSToomas Soome 	}
357d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
358d7802caeSToomas Soome 	if (blkdev == NULL)
359d7802caeSToomas Soome 		return;
360d7802caeSToomas Soome 
361d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
362d7802caeSToomas Soome 	if (fp == NULL) {
363d7802caeSToomas Soome 		perror("fopen");
364d7802caeSToomas Soome 		free(blkdev);
365d7802caeSToomas Soome 		return;
366d7802caeSToomas Soome 	}
367d7802caeSToomas Soome 
368d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
369d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
370d7802caeSToomas Soome 	(void) fclose(fp);
371d7802caeSToomas Soome 	if (ret == 0)
372d7802caeSToomas Soome 		path = mp.mnt_mountp;
373d7802caeSToomas Soome 	else
374d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
375d7802caeSToomas Soome 
376d7802caeSToomas Soome 	free(blkdev);
377d7802caeSToomas Soome 	if (path == NULL)
378d7802caeSToomas Soome 		return;
379d7802caeSToomas Soome 
380d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, "/EFI") < 0) {
3810c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
382d7802caeSToomas Soome 		return;
3830c946d80SToomas Soome 	}
3840c946d80SToomas Soome 
385d7802caeSToomas Soome 	ret = mkdir(file, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
386d7802caeSToomas Soome 	if (ret == 0 || errno == EEXIST) {
387d7802caeSToomas Soome 		free(file);
388d7802caeSToomas Soome 		if (asprintf(&file, "%s%s", path, "/EFI/Boot") < 0) {
389d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
390d7802caeSToomas Soome 			return;
391d7802caeSToomas Soome 		}
392d7802caeSToomas Soome 		ret = mkdir(file,
393d7802caeSToomas Soome 		    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
394d7802caeSToomas Soome 		if (errno == EEXIST)
395d7802caeSToomas Soome 			ret = 0;
396d7802caeSToomas Soome 	}
397d7802caeSToomas Soome 	free(file);
398d7802caeSToomas Soome 	if (ret < 0) {
399d7802caeSToomas Soome 		perror("mkdir");
400d7802caeSToomas Soome 		return;
4010c946d80SToomas Soome 	}
4020c946d80SToomas Soome 
403d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
404d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
405d7802caeSToomas Soome 		return;
406d7802caeSToomas Soome 	}
407d7802caeSToomas Soome 
408d7802caeSToomas Soome 	/* Write stage file. Should create temp file and rename. */
409d7802caeSToomas Soome 	(void) chmod(file, S_IRUSR | S_IWUSR);
410d7802caeSToomas Soome 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
411d7802caeSToomas Soome 	if (fd != -1) {
412d7802caeSToomas Soome 		ret = write_out(fd, bblock->buf, bblock->buf_size, 0);
413d7802caeSToomas Soome 		if (ret == BC_SUCCESS) {
414d7802caeSToomas Soome 			(void) fprintf(stdout,
4159937ff19SToomas Soome 			    gettext("bootblock written to %s\n\n"), file);
416d7802caeSToomas Soome 		} else {
417d7802caeSToomas Soome 			(void) fprintf(stdout,
418d7802caeSToomas Soome 			    gettext("error while writing %s\n"), file);
419d7802caeSToomas Soome 		}
420d7802caeSToomas Soome 		(void) fchmod(fd, S_IRUSR | S_IRGRP | S_IROTH);
421d7802caeSToomas Soome 		(void) close(fd);
422d7802caeSToomas Soome 	}
423d7802caeSToomas Soome 	free(file);
424d7802caeSToomas Soome }
425d7802caeSToomas Soome 
426d7802caeSToomas Soome /*
427d7802caeSToomas Soome  * MBR setup only depends on write_mbr toggle.
428d7802caeSToomas Soome  */
429d7802caeSToomas Soome static bool
compare_mbr_cb(struct partlist * plist)430d7802caeSToomas Soome compare_mbr_cb(struct partlist *plist)
431d7802caeSToomas Soome {
432d7802caeSToomas Soome 	/* get confirmation for -m */
433d7802caeSToomas Soome 	if (write_mbr && !force_mbr) {
434d7802caeSToomas Soome 		(void) fprintf(stdout, gettext("Updating master boot sector "
435d7802caeSToomas Soome 		    "destroys existing boot managers (if any).\n"
436d7802caeSToomas Soome 		    "continue (y/n)? "));
437d7802caeSToomas Soome 		if (!yes()) {
438d7802caeSToomas Soome 			write_mbr = false;
439d7802caeSToomas Soome 			(void) fprintf(stdout, gettext("master boot sector "
440d7802caeSToomas Soome 			    "not updated\n"));
441d7802caeSToomas Soome 		}
442d7802caeSToomas Soome 	}
443d7802caeSToomas Soome 	if (write_mbr)
444d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
445d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
446d7802caeSToomas Soome 	return (write_mbr);
447d7802caeSToomas Soome }
448d7802caeSToomas Soome 
449d7802caeSToomas Soome /*
4509937ff19SToomas Soome  * VBR setup is done in pair with stage2.
451d7802caeSToomas Soome  */
452d7802caeSToomas Soome static bool
compare_stage1_cb(struct partlist * plist)453d7802caeSToomas Soome compare_stage1_cb(struct partlist *plist)
454d7802caeSToomas Soome {
4559937ff19SToomas Soome 	if (write_vbr) {
456*6b02bf31SToomas Soome 		(void) printf("%s is newer than one in %s\n",
457*6b02bf31SToomas Soome 		    plist->pl_src_name, plist->pl_devname);
4589937ff19SToomas Soome 	}
4599937ff19SToomas Soome 	return (write_vbr);
460d7802caeSToomas Soome }
461d7802caeSToomas Soome 
462d7802caeSToomas Soome /*
463d7802caeSToomas Soome  * Return true if we can update, false if not.
464d7802caeSToomas Soome  */
465d7802caeSToomas Soome static bool
compare_einfo_cb(struct partlist * plist)466d7802caeSToomas Soome compare_einfo_cb(struct partlist *plist)
467d7802caeSToomas Soome {
468d7802caeSToomas Soome 	ib_bootblock_t *bblock, *bblock_file;
469d7802caeSToomas Soome 	bblk_einfo_t *einfo, *einfo_file;
470d7802caeSToomas Soome 	bblk_hs_t bblock_hs;
471d7802caeSToomas Soome 	bool rv;
472d7802caeSToomas Soome 
47346d70dceSToomas Soome 	bblock_file = plist->pl_src_data;
47446d70dceSToomas Soome 	if (bblock_file == NULL)
47546d70dceSToomas Soome 		return (false);	/* source is missing, cannot update */
47646d70dceSToomas Soome 
477d7802caeSToomas Soome 	bblock = plist->pl_stage;
4789937ff19SToomas Soome 	if (bblock == NULL ||
4799937ff19SToomas Soome 	    bblock->extra == NULL ||
4809937ff19SToomas Soome 	    bblock->extra_size == 0) {
4819937ff19SToomas Soome 		if (plist->pl_type == IB_BBLK_STAGE2)
4829937ff19SToomas Soome 			write_vbr = true;
483d7802caeSToomas Soome 		return (true);
4849937ff19SToomas Soome 	}
485d7802caeSToomas Soome 
486d7802caeSToomas Soome 	einfo = find_einfo(bblock->extra, bblock->extra_size);
487d7802caeSToomas Soome 	if (einfo == NULL) {
488d7802caeSToomas Soome 		BOOT_DEBUG("No extended information available on disk\n");
4899937ff19SToomas Soome 		if (plist->pl_type == IB_BBLK_STAGE2)
4909937ff19SToomas Soome 			write_vbr = true;
491d7802caeSToomas Soome 		return (true);
492d7802caeSToomas Soome 	}
493d7802caeSToomas Soome 
494d7802caeSToomas Soome 	einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
495d7802caeSToomas Soome 	if (einfo_file == NULL) {
496d7802caeSToomas Soome 		/*
497d7802caeSToomas Soome 		 * loader bootblock is versioned. missing version means
498d7802caeSToomas Soome 		 * probably incompatible block. installboot can not install
499d7802caeSToomas Soome 		 * grub, for example.
500d7802caeSToomas Soome 		 */
5010c946d80SToomas Soome 		(void) fprintf(stderr,
502d7802caeSToomas Soome 		    gettext("ERROR: non versioned bootblock in file\n"));
503d7802caeSToomas Soome 		return (false);
504d7802caeSToomas Soome 	} else {
505d7802caeSToomas Soome 		if (update_str == NULL) {
506d7802caeSToomas Soome 			update_str = einfo_get_string(einfo_file);
507d7802caeSToomas Soome 			do_version = true;
508d7802caeSToomas Soome 		}
5090c946d80SToomas Soome 	}
5100c946d80SToomas Soome 
511d7802caeSToomas Soome 	if (!do_version || update_str == NULL) {
512d7802caeSToomas Soome 		(void) fprintf(stderr,
513d7802caeSToomas Soome 		    gettext("WARNING: target device %s has a "
514d7802caeSToomas Soome 		    "versioned bootblock that is going to be overwritten by a "
515d7802caeSToomas Soome 		    "non versioned one\n"), plist->pl_devname);
5169937ff19SToomas Soome 		if (plist->pl_type == IB_BBLK_STAGE2)
5179937ff19SToomas Soome 			write_vbr = true;
518d7802caeSToomas Soome 		return (true);
519d7802caeSToomas Soome 	}
5200c946d80SToomas Soome 
521d7802caeSToomas Soome 	if (force_update) {
522d7802caeSToomas Soome 		BOOT_DEBUG("Forcing update of %s bootblock\n",
523d7802caeSToomas Soome 		    plist->pl_devname);
5249937ff19SToomas Soome 		if (plist->pl_type == IB_BBLK_STAGE2)
5259937ff19SToomas Soome 			write_vbr = true;
526d7802caeSToomas Soome 		return (true);
527d7802caeSToomas Soome 	}
5280c946d80SToomas Soome 
529d7802caeSToomas Soome 	BOOT_DEBUG("Ready to check installed version vs %s\n", update_str);
5300c946d80SToomas Soome 
531d7802caeSToomas Soome 	bblock_hs.src_buf = (unsigned char *)bblock_file->file;
532d7802caeSToomas Soome 	bblock_hs.src_size = bblock_file->file_size;
533d7802caeSToomas Soome 
534d7802caeSToomas Soome 	rv = einfo_should_update(einfo, &bblock_hs, update_str);
535d7802caeSToomas Soome 	if (rv == false) {
5369937ff19SToomas Soome 		(void) fprintf(stderr, gettext("Bootblock version installed "
537d7802caeSToomas Soome 		    "on %s is more recent or identical to\n%s\n"
5389937ff19SToomas Soome 		    "Use -F to override or install without the -u option.\n\n"),
539d7802caeSToomas Soome 		    plist->pl_devname, plist->pl_src_name);
540d7802caeSToomas Soome 	} else {
541d7802caeSToomas Soome 		(void) printf("%s is newer than one in %s\n",
542d7802caeSToomas Soome 		    plist->pl_src_name, plist->pl_devname);
5439937ff19SToomas Soome 		if (plist->pl_type == IB_BBLK_STAGE2)
5449937ff19SToomas Soome 			write_vbr = true;
545d7802caeSToomas Soome 	}
546d7802caeSToomas Soome 	return (rv);
547d7802caeSToomas Soome }
548d7802caeSToomas Soome 
549d7802caeSToomas Soome static bool
read_stage1_cb(struct partlist * plist)550d7802caeSToomas Soome read_stage1_cb(struct partlist *plist)
551d7802caeSToomas Soome {
552d7802caeSToomas Soome 	int fd;
553d7802caeSToomas Soome 	bool rv = false;
554d7802caeSToomas Soome 
555d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
556d7802caeSToomas Soome 		return (rv);
557d7802caeSToomas Soome 
558d7802caeSToomas Soome 	if (plist->pl_stage == NULL)
559d7802caeSToomas Soome 		plist->pl_stage = calloc(1, sector_size);
560d7802caeSToomas Soome 
561d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
562d7802caeSToomas Soome 		perror("calloc");
563d7802caeSToomas Soome 		goto done;
564d7802caeSToomas Soome 	}
5650c946d80SToomas Soome 
566d7802caeSToomas Soome 	if (pread(fd, plist->pl_stage, sector_size, 0) == -1) {
567d7802caeSToomas Soome 		perror("pread");
568d7802caeSToomas Soome 		goto done;
569d7802caeSToomas Soome 	}
570d7802caeSToomas Soome 	rv = true;
571d7802caeSToomas Soome done:
5720c946d80SToomas Soome 	(void) close(fd);
573d7802caeSToomas Soome 	return (rv);
574d7802caeSToomas Soome }
5750c946d80SToomas Soome 
576d7802caeSToomas Soome static bool
read_stage1_bbl_cb(struct partlist * plist)577d7802caeSToomas Soome read_stage1_bbl_cb(struct partlist *plist)
578d7802caeSToomas Soome {
579d7802caeSToomas Soome 	int fd;
580d7802caeSToomas Soome 	void *data;
581d7802caeSToomas Soome 	bool rv = false;
582d7802caeSToomas Soome 
583d7802caeSToomas Soome 	data = malloc(SECTOR_SIZE);
584d7802caeSToomas Soome 	if (data == NULL)
585d7802caeSToomas Soome 		return (rv);
586d7802caeSToomas Soome 
587d7802caeSToomas Soome 	/* read the stage1 file from filesystem */
588d7802caeSToomas Soome 	fd = open(plist->pl_src_name, O_RDONLY);
589d7802caeSToomas Soome 	if (fd == -1 ||
590d7802caeSToomas Soome 	    read(fd, data, SECTOR_SIZE) != SECTOR_SIZE) {
591d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
592d7802caeSToomas Soome 		    plist->pl_src_name);
593d7802caeSToomas Soome 		free(data);
594d7802caeSToomas Soome 		if (fd != -1)
595d7802caeSToomas Soome 			(void) close(fd);
596d7802caeSToomas Soome 		return (rv);
597d7802caeSToomas Soome 	}
598d7802caeSToomas Soome 
599d7802caeSToomas Soome 	plist->pl_src_data = data;
6000c946d80SToomas Soome 	(void) close(fd);
601d7802caeSToomas Soome 	return (true);
6020c946d80SToomas Soome }
6030c946d80SToomas Soome 
604d7802caeSToomas Soome static bool
read_stage2_cb(struct partlist * plist)605d7802caeSToomas Soome read_stage2_cb(struct partlist *plist)
6060c946d80SToomas Soome {
607d7802caeSToomas Soome 	ib_device_t		*device;
608d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
609d7802caeSToomas Soome 	int			fd;
6100c946d80SToomas Soome 	uint32_t		size, offset;
6110c946d80SToomas Soome 	uint32_t		buf_size;
6120c946d80SToomas Soome 	uint32_t		mboot_off;
6130c946d80SToomas Soome 	multiboot_header_t	*mboot;
614b97b1727SToomas Soome 	size_t			scan_size;
6150c946d80SToomas Soome 
616d7802caeSToomas Soome 	bblock = calloc(1, sizeof (ib_bootblock_t));
617d7802caeSToomas Soome 	if (bblock == NULL)
618d7802caeSToomas Soome 		return (false);
6190c946d80SToomas Soome 
620d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
621d7802caeSToomas Soome 		free(bblock);
622d7802caeSToomas Soome 		return (false);
6230c946d80SToomas Soome 	}
6240c946d80SToomas Soome 
625d7802caeSToomas Soome 	device = plist->pl_device;
626d7802caeSToomas Soome 	plist->pl_stage = bblock;
627d7802caeSToomas Soome 	offset = device->stage.offset * SECTOR_SIZE;
628b97b1727SToomas Soome 	scan_size = MIN(sizeof (mboot_scan),
629b97b1727SToomas Soome 	    (device->stage.size - device->stage.offset) * sector_size);
630d7802caeSToomas Soome 
631b97b1727SToomas Soome 	if (read_in(fd, mboot_scan, scan_size, offset)
6320c946d80SToomas Soome 	    != BC_SUCCESS) {
6330c946d80SToomas Soome 		BOOT_DEBUG("Error reading bootblock area\n");
6340c946d80SToomas Soome 		perror("read");
635d7802caeSToomas Soome 		(void) close(fd);
636d7802caeSToomas Soome 		return (false);
6370c946d80SToomas Soome 	}
6380c946d80SToomas Soome 
6390c946d80SToomas Soome 	/* No multiboot means no chance of knowing bootblock size */
640b97b1727SToomas Soome 	if (find_multiboot(mboot_scan, scan_size, &mboot_off)
6410c946d80SToomas Soome 	    != BC_SUCCESS) {
6420c946d80SToomas Soome 		BOOT_DEBUG("Unable to find multiboot header\n");
643d7802caeSToomas Soome 		(void) close(fd);
644d7802caeSToomas Soome 		return (false);
6450c946d80SToomas Soome 	}
6460c946d80SToomas Soome 	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
6470c946d80SToomas Soome 
6480c946d80SToomas Soome 	/*
6490c946d80SToomas Soome 	 * make sure mboot has sane values
6500c946d80SToomas Soome 	 */
6510c946d80SToomas Soome 	if (mboot->load_end_addr == 0 ||
652d7802caeSToomas Soome 	    mboot->load_end_addr < mboot->load_addr) {
653d7802caeSToomas Soome 		(void) close(fd);
654d7802caeSToomas Soome 		return (false);
655d7802caeSToomas Soome 	}
6560c946d80SToomas Soome 
6570c946d80SToomas Soome 	/*
6580c946d80SToomas Soome 	 * Currently, the amount of space reserved for extra information
6590c946d80SToomas Soome 	 * is "fixed". We may have to scan for the terminating extra payload
6600c946d80SToomas Soome 	 * in the future.
6610c946d80SToomas Soome 	 */
6620c946d80SToomas Soome 	size = mboot->load_end_addr - mboot->load_addr;
6630c946d80SToomas Soome 	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
6640c946d80SToomas Soome 	bblock->file_size = size;
6650c946d80SToomas Soome 
6660c946d80SToomas Soome 	bblock->buf = malloc(buf_size);
6670c946d80SToomas Soome 	if (bblock->buf == NULL) {
6680c946d80SToomas Soome 		BOOT_DEBUG("Unable to allocate enough memory to read"
6690c946d80SToomas Soome 		    " the extra bootblock from the disk\n");
6700c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
671d7802caeSToomas Soome 		(void) close(fd);
672d7802caeSToomas Soome 		return (false);
6730c946d80SToomas Soome 	}
6740c946d80SToomas Soome 	bblock->buf_size = buf_size;
6750c946d80SToomas Soome 
676d7802caeSToomas Soome 	if (read_in(fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
6770c946d80SToomas Soome 		BOOT_DEBUG("Error reading the bootblock\n");
6780c946d80SToomas Soome 		(void) free(bblock->buf);
6790c946d80SToomas Soome 		bblock->buf = NULL;
680d7802caeSToomas Soome 		(void) close(fd);
681d7802caeSToomas Soome 		return (false);
6820c946d80SToomas Soome 	}
6830c946d80SToomas Soome 
6840c946d80SToomas Soome 	/* Update pointers. */
6850c946d80SToomas Soome 	bblock->file = bblock->buf;
6860c946d80SToomas Soome 	bblock->mboot_off = mboot_off;
6870c946d80SToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
6880c946d80SToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
6890c946d80SToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
6900c946d80SToomas Soome 
6910c946d80SToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
6920c946d80SToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
6930c946d80SToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
6940c946d80SToomas Soome 
695d7802caeSToomas Soome 	return (true);
6960c946d80SToomas Soome }
6970c946d80SToomas Soome 
698d7802caeSToomas Soome static bool
read_einfo_file_cb(struct partlist * plist)699d7802caeSToomas Soome read_einfo_file_cb(struct partlist *plist)
7000c946d80SToomas Soome {
70146d70dceSToomas Soome 	int rc;
70246d70dceSToomas Soome 	void *stage;
70346d70dceSToomas Soome 
70446d70dceSToomas Soome 	stage = calloc(1, sizeof (ib_bootblock_t));
70546d70dceSToomas Soome 	if (stage == NULL)
706d7802caeSToomas Soome 		return (false);
7070c946d80SToomas Soome 
70846d70dceSToomas Soome 	rc =  read_bootblock_from_file(plist->pl_devname, stage);
70946d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
71046d70dceSToomas Soome 		free(stage);
71146d70dceSToomas Soome 		stage = NULL;
71246d70dceSToomas Soome 	}
71346d70dceSToomas Soome 	plist->pl_stage = stage;
71446d70dceSToomas Soome 	return (rc == BC_SUCCESS);
715d7802caeSToomas Soome }
7160c946d80SToomas Soome 
717d7802caeSToomas Soome static bool
read_stage2_file_cb(struct partlist * plist)718d7802caeSToomas Soome read_stage2_file_cb(struct partlist *plist)
719d7802caeSToomas Soome {
72046d70dceSToomas Soome 	int rc;
72146d70dceSToomas Soome 	void *data;
72246d70dceSToomas Soome 
72346d70dceSToomas Soome 	data = calloc(1, sizeof (ib_bootblock_t));
72446d70dceSToomas Soome 	if (data == NULL)
725d7802caeSToomas Soome 		return (false);
7260c946d80SToomas Soome 
72746d70dceSToomas Soome 	rc = read_bootblock_from_file(plist->pl_src_name, data);
72846d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
72946d70dceSToomas Soome 		free(data);
73046d70dceSToomas Soome 		data = NULL;
73146d70dceSToomas Soome 	}
73246d70dceSToomas Soome 	plist->pl_src_data = data;
73346d70dceSToomas Soome 	return (rc == BC_SUCCESS);
734d7802caeSToomas Soome }
7350c946d80SToomas Soome 
736d7802caeSToomas Soome /*
737d7802caeSToomas Soome  * convert /dev/rdsk/... to /dev/dsk/...
738d7802caeSToomas Soome  */
739d7802caeSToomas Soome static char *
make_blkdev(const char * path)740d7802caeSToomas Soome make_blkdev(const char *path)
741d7802caeSToomas Soome {
742d7802caeSToomas Soome 	char *tmp;
743d7802caeSToomas Soome 	char *ptr = strdup(path);
7440c946d80SToomas Soome 
745d7802caeSToomas Soome 	if (ptr == NULL)
746d7802caeSToomas Soome 		return (ptr);
7470c946d80SToomas Soome 
748d7802caeSToomas Soome 	tmp = strstr(ptr, "rdsk");
749d7802caeSToomas Soome 	if (tmp == NULL) {
750d7802caeSToomas Soome 		free(ptr);
751d7802caeSToomas Soome 		return (NULL); /* Something is very wrong */
7520c946d80SToomas Soome 	}
753d7802caeSToomas Soome 	/* This is safe because we do shorten the string */
754d7802caeSToomas Soome 	(void) memmove(tmp, tmp + 1, strlen(tmp));
755d7802caeSToomas Soome 	return (ptr);
756d7802caeSToomas Soome }
7570c946d80SToomas Soome 
758d7802caeSToomas Soome /*
759d7802caeSToomas Soome  * Try to mount ESP and read boot program.
760d7802caeSToomas Soome  */
761d7802caeSToomas Soome static bool
read_einfo_esp_cb(struct partlist * plist)762d7802caeSToomas Soome read_einfo_esp_cb(struct partlist *plist)
763d7802caeSToomas Soome {
764d7802caeSToomas Soome 	fstyp_handle_t fhdl;
765d7802caeSToomas Soome 	const char *fident;
766d7802caeSToomas Soome 	char *blkdev, *path, *file;
767d7802caeSToomas Soome 	bool rv = false;
768d7802caeSToomas Soome 	FILE *fp;
769d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
770d7802caeSToomas Soome 	int fd, ret;
7710c946d80SToomas Soome 
772d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
773d7802caeSToomas Soome 		return (rv);
7740c946d80SToomas Soome 
775d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
776d7802caeSToomas Soome 		(void) close(fd);
777d7802caeSToomas Soome 		return (rv);
778d7802caeSToomas Soome 	}
7790c946d80SToomas Soome 
780d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
781d7802caeSToomas Soome 		fstyp_fini(fhdl);
782d7802caeSToomas Soome 		(void) close(fd);
783d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
784d7802caeSToomas Soome 		    "system type\n"));
785d7802caeSToomas Soome 		return (rv);
786d7802caeSToomas Soome 	}
7870c946d80SToomas Soome 
788d7802caeSToomas Soome 	/* We only do expect pcfs. */
789d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_PCFS) != 0) {
790d7802caeSToomas Soome 		(void) fprintf(stderr,
791d7802caeSToomas Soome 		    gettext("File system %s is not supported.\n"), fident);
792d7802caeSToomas Soome 		fstyp_fini(fhdl);
793d7802caeSToomas Soome 		(void) close(fd);
794d7802caeSToomas Soome 		return (rv);
795d7802caeSToomas Soome 	}
796d7802caeSToomas Soome 	fstyp_fini(fhdl);
797d7802caeSToomas Soome 	(void) close(fd);
798d7802caeSToomas Soome 
799d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
800d7802caeSToomas Soome 	if (blkdev == NULL)
801d7802caeSToomas Soome 		return (rv);
802d7802caeSToomas Soome 
803d7802caeSToomas Soome 	/* mount ESP if needed, read boot program(s) and unmount. */
804d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
805d7802caeSToomas Soome 	if (fp == NULL) {
806d7802caeSToomas Soome 		perror("fopen");
807d7802caeSToomas Soome 		free(blkdev);
808d7802caeSToomas Soome 		return (rv);
8090c946d80SToomas Soome 	}
8100c946d80SToomas Soome 
811d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
812d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
813d7802caeSToomas Soome 	(void) fclose(fp);
814d7802caeSToomas Soome 	if (ret == 0)
815d7802caeSToomas Soome 		path = mp.mnt_mountp;
816d7802caeSToomas Soome 	else
817d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
818d7802caeSToomas Soome 
819d7802caeSToomas Soome 	free(blkdev);
820d7802caeSToomas Soome 	if (path == NULL)
821d7802caeSToomas Soome 		return (rv);
822d7802caeSToomas Soome 
823d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
824d7802caeSToomas Soome 		return (rv);
825d7802caeSToomas Soome 	}
826d7802caeSToomas Soome 
827d7802caeSToomas Soome 	plist->pl_stage = calloc(1, sizeof (ib_bootblock_t));
828d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
829d7802caeSToomas Soome 		free(file);
830d7802caeSToomas Soome 		return (rv);
831d7802caeSToomas Soome 	}
832d7802caeSToomas Soome 	if (read_bootblock_from_file(file, plist->pl_stage) != BC_SUCCESS) {
833d7802caeSToomas Soome 		free(plist->pl_stage);
834d7802caeSToomas Soome 		plist->pl_stage = NULL;
835d7802caeSToomas Soome 	} else {
836d7802caeSToomas Soome 		rv = true;
837d7802caeSToomas Soome 	}
838d7802caeSToomas Soome 
839d7802caeSToomas Soome 	free(file);
840d7802caeSToomas Soome 	return (rv);
8410c946d80SToomas Soome }
8420c946d80SToomas Soome 
843d7802caeSToomas Soome static void
print_stage1_cb(struct partlist * plist)844d7802caeSToomas Soome print_stage1_cb(struct partlist *plist)
8450c946d80SToomas Soome {
846d7802caeSToomas Soome 	struct mboot *mbr;
847d7802caeSToomas Soome 	struct ipart *part;
848d7802caeSToomas Soome 	mbr_type_t type = MBR_TYPE_UNKNOWN;
849d7802caeSToomas Soome 	bool pmbr = false;
850d7802caeSToomas Soome 	char *label;
8510c946d80SToomas Soome 
852d7802caeSToomas Soome 	mbr = plist->pl_stage;
8530c946d80SToomas Soome 
854d7802caeSToomas Soome 	if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
855d7802caeSToomas Soome 		type = MBR_TYPE_GRUB1;
856d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
857d7802caeSToomas Soome 		type = MBR_TYPE_LOADER;
858d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
859d7802caeSToomas Soome 		type = MBR_TYPE_LOADER_JOYENT;
860d7802caeSToomas Soome 	}
8610c946d80SToomas Soome 
862d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
863d7802caeSToomas Soome 	for (int i = 0; i < FD_NUMPART; i++) {
864d7802caeSToomas Soome 		if (part[i].systid == EFI_PMBR)
865d7802caeSToomas Soome 			pmbr = true;
866d7802caeSToomas Soome 	}
8670c946d80SToomas Soome 
868d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR)
869d7802caeSToomas Soome 		label = pmbr ? "PMBR" : "MBR";
870d7802caeSToomas Soome 	else
871d7802caeSToomas Soome 		label = "VBR";
8720c946d80SToomas Soome 
873d7802caeSToomas Soome 	printf("%s block from %s:\n", label, plist->pl_devname);
8740c946d80SToomas Soome 
875d7802caeSToomas Soome 	switch (type) {
876d7802caeSToomas Soome 	case MBR_TYPE_UNKNOWN:
877d7802caeSToomas Soome 		printf("Format: unknown\n");
878d7802caeSToomas Soome 		break;
879d7802caeSToomas Soome 	case MBR_TYPE_GRUB1:
880d7802caeSToomas Soome 		printf("Format: grub1\n");
881d7802caeSToomas Soome 		break;
882d7802caeSToomas Soome 	case MBR_TYPE_LOADER:
883d7802caeSToomas Soome 		printf("Format: loader (illumos)\n");
884d7802caeSToomas Soome 		break;
885d7802caeSToomas Soome 	case MBR_TYPE_LOADER_JOYENT:
886d7802caeSToomas Soome 		printf("Format: loader (joyent)\n");
887d7802caeSToomas Soome 		break;
8880c946d80SToomas Soome 	}
8890c946d80SToomas Soome 
890d7802caeSToomas Soome 	printf("Signature: 0x%hx (%s)\n", mbr->signature,
891d7802caeSToomas Soome 	    mbr->signature == MBB_MAGIC ? "valid" : "invalid");
8920c946d80SToomas Soome 
893d7802caeSToomas Soome 	printf("UniqueMBRDiskSignature: %#lx\n",
894d7802caeSToomas Soome 	    *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
895d7802caeSToomas Soome 
896d7802caeSToomas Soome 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
897d7802caeSToomas Soome 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
898d7802caeSToomas Soome 
899d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_LBA: %llu\n",
900d7802caeSToomas Soome 		    *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
901d7802caeSToomas Soome 
902d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
903d7802caeSToomas Soome 		    *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
904d7802caeSToomas Soome 
905d7802caeSToomas Soome 		uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
906d7802caeSToomas Soome 		    uuid);
907d7802caeSToomas Soome 
908d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
909d7802caeSToomas Soome 	}
910d7802caeSToomas Soome 	printf("\n");
9110c946d80SToomas Soome }
9120c946d80SToomas Soome 
913d7802caeSToomas Soome static void
print_einfo_cb(struct partlist * plist)914d7802caeSToomas Soome print_einfo_cb(struct partlist *plist)
9150c946d80SToomas Soome {
916d7802caeSToomas Soome 	uint8_t flags = 0;
917d7802caeSToomas Soome 	ib_bootblock_t *bblock;
918d7802caeSToomas Soome 	bblk_einfo_t *einfo = NULL;
919d7802caeSToomas Soome 	const char *filepath;
920d7802caeSToomas Soome 
921d7802caeSToomas Soome 	/* No stage, get out. */
922d7802caeSToomas Soome 	bblock = plist->pl_stage;
923d7802caeSToomas Soome 	if (bblock == NULL)
924d7802caeSToomas Soome 		return;
9250c946d80SToomas Soome 
926d7802caeSToomas Soome 	if (plist->pl_device->stage.path == NULL)
927d7802caeSToomas Soome 		filepath = "";
928d7802caeSToomas Soome 	else
929d7802caeSToomas Soome 		filepath = plist->pl_device->stage.path;
9300c946d80SToomas Soome 
931d7802caeSToomas Soome 	printf("Boot block from %s:%s\n", plist->pl_devname, filepath);
9320c946d80SToomas Soome 
933d7802caeSToomas Soome 	if (bblock->extra != NULL)
934d7802caeSToomas Soome 		einfo = find_einfo(bblock->extra, bblock->extra_size);
9350c946d80SToomas Soome 
936d7802caeSToomas Soome 	if (einfo == NULL) {
937d7802caeSToomas Soome 		(void) fprintf(stderr,
938d7802caeSToomas Soome 		    gettext("No extended information found.\n\n"));
939d7802caeSToomas Soome 		return;
940d7802caeSToomas Soome 	}
9410c946d80SToomas Soome 
942d7802caeSToomas Soome 	/* Print the extended information. */
943d7802caeSToomas Soome 	if (strip)
944d7802caeSToomas Soome 		flags |= EINFO_EASY_PARSE;
945d7802caeSToomas Soome 	if (verbose_dump)
946d7802caeSToomas Soome 		flags |= EINFO_PRINT_HEADER;
947d7802caeSToomas Soome 
948d7802caeSToomas Soome 	print_einfo(flags, einfo, bblock->extra_size);
949d7802caeSToomas Soome 	printf("\n");
9500c946d80SToomas Soome }
9510c946d80SToomas Soome 
952d7802caeSToomas Soome static size_t
get_media_info(int fd)953d7802caeSToomas Soome get_media_info(int fd)
9540c946d80SToomas Soome {
955d7802caeSToomas Soome 	struct dk_minfo disk_info;
9560c946d80SToomas Soome 
957d7802caeSToomas Soome 	if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
958d7802caeSToomas Soome 		return (SECTOR_SIZE);
9590c946d80SToomas Soome 
960d7802caeSToomas Soome 	return (disk_info.dki_lbsize);
961d7802caeSToomas Soome }
962d7802caeSToomas Soome 
963d7802caeSToomas Soome static struct partlist *
partlist_alloc(void)964d7802caeSToomas Soome partlist_alloc(void)
965d7802caeSToomas Soome {
966d7802caeSToomas Soome 	struct partlist *pl;
967d7802caeSToomas Soome 
968d7802caeSToomas Soome 	if ((pl = calloc(1, sizeof (*pl))) == NULL) {
969d7802caeSToomas Soome 		perror("calloc");
970d7802caeSToomas Soome 		return (NULL);
9710c946d80SToomas Soome 	}
9720c946d80SToomas Soome 
973d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
974d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
975d7802caeSToomas Soome 		perror("calloc");
976d7802caeSToomas Soome 		free(pl);
977d7802caeSToomas Soome 		return (NULL);
978d7802caeSToomas Soome 	}
979d7802caeSToomas Soome 
980d7802caeSToomas Soome 	return (pl);
981d7802caeSToomas Soome }
982d7802caeSToomas Soome 
983d7802caeSToomas Soome static void
partlist_free(struct partlist * pl)984d7802caeSToomas Soome partlist_free(struct partlist *pl)
985d7802caeSToomas Soome {
986d7802caeSToomas Soome 	ib_bootblock_t *bblock;
987d7802caeSToomas Soome 	ib_device_t *device;
988d7802caeSToomas Soome 
989d7802caeSToomas Soome 	switch (pl->pl_type) {
990d7802caeSToomas Soome 	case IB_BBLK_MBR:
991d7802caeSToomas Soome 	case IB_BBLK_STAGE1:
992d7802caeSToomas Soome 		free(pl->pl_stage);
993d7802caeSToomas Soome 		break;
994d7802caeSToomas Soome 	default:
995d7802caeSToomas Soome 		if (pl->pl_stage != NULL) {
996d7802caeSToomas Soome 			bblock = pl->pl_stage;
997d7802caeSToomas Soome 			free(bblock->buf);
998d7802caeSToomas Soome 			free(bblock);
9990c946d80SToomas Soome 		}
10000c946d80SToomas Soome 	}
1001d7802caeSToomas Soome 
1002d7802caeSToomas Soome 	/* umount the stage fs. */
1003d7802caeSToomas Soome 	if (pl->pl_device->stage.mntpnt != NULL) {
1004d7802caeSToomas Soome 		if (umount(pl->pl_device->stage.mntpnt) == 0)
1005d7802caeSToomas Soome 			(void) rmdir(pl->pl_device->stage.mntpnt);
1006d7802caeSToomas Soome 		free(pl->pl_device->stage.mntpnt);
10070c946d80SToomas Soome 	}
1008d7802caeSToomas Soome 	device = pl->pl_device;
1009d7802caeSToomas Soome 	free(device->target.path);
1010d7802caeSToomas Soome 	free(pl->pl_device);
10110c946d80SToomas Soome 
1012d7802caeSToomas Soome 	free(pl->pl_src_data);
1013d7802caeSToomas Soome 	free(pl->pl_devname);
1014d7802caeSToomas Soome 	free(pl);
1015d7802caeSToomas Soome }
10160c946d80SToomas Soome 
1017d7802caeSToomas Soome static bool
probe_fstyp(ib_data_t * data)1018d7802caeSToomas Soome probe_fstyp(ib_data_t *data)
1019d7802caeSToomas Soome {
1020d7802caeSToomas Soome 	fstyp_handle_t fhdl;
1021d7802caeSToomas Soome 	const char *fident;
1022d7802caeSToomas Soome 	char *ptr;
1023d7802caeSToomas Soome 	int fd;
1024d7802caeSToomas Soome 	bool rv = false;
1025d7802caeSToomas Soome 
1026d7802caeSToomas Soome 	/* Record partition id */
1027d7802caeSToomas Soome 	ptr = strrchr(data->target.path, 'p');
1028d7802caeSToomas Soome 	if (ptr == NULL)
1029d7802caeSToomas Soome 		ptr = strrchr(data->target.path, 's');
1030d7802caeSToomas Soome 	data->target.id = atoi(++ptr);
1031d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) == -1)
1032d7802caeSToomas Soome 		return (rv);
1033d7802caeSToomas Soome 
1034d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
1035d7802caeSToomas Soome 		(void) close(fd);
1036d7802caeSToomas Soome 		return (rv);
1037d7802caeSToomas Soome 	}
1038d7802caeSToomas Soome 
1039d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1040d7802caeSToomas Soome 		fstyp_fini(fhdl);
1041d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
1042d7802caeSToomas Soome 		    "system type\n"));
1043d7802caeSToomas Soome 		(void) close(fd);
1044d7802caeSToomas Soome 		return (rv);
1045d7802caeSToomas Soome 	}
1046d7802caeSToomas Soome 
1047d7802caeSToomas Soome 	rv = true;
1048d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_ZFS) == 0)
1049d7802caeSToomas Soome 		data->target.fstype = IB_FS_ZFS;
1050d7802caeSToomas Soome 	else if (strcmp(fident, MNTTYPE_UFS) == 0) {
1051d7802caeSToomas Soome 		data->target.fstype = IB_FS_UFS;
1052d7802caeSToomas Soome 	} else if (strcmp(fident, MNTTYPE_PCFS) == 0) {
1053d7802caeSToomas Soome 		data->target.fstype = IB_FS_PCFS;
1054d8d05a42SToomas Soome 		/* with pcfs we always write MBR */
1055d8d05a42SToomas Soome 		force_mbr = true;
1056d8d05a42SToomas Soome 		write_mbr = true;
1057d7802caeSToomas Soome 	} else {
1058d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("File system %s is not "
1059d7802caeSToomas Soome 		    "supported by loader\n"), fident);
1060d7802caeSToomas Soome 		rv = false;
1061d7802caeSToomas Soome 	}
1062d7802caeSToomas Soome 	fstyp_fini(fhdl);
1063d7802caeSToomas Soome 	(void) close(fd);
1064d7802caeSToomas Soome 	return (rv);
10650c946d80SToomas Soome }
10660c946d80SToomas Soome 
1067d7802caeSToomas Soome static bool
get_slice(ib_data_t * data,struct partlist * pl,struct dk_gpt * vtoc,uint16_t tag)1068d7802caeSToomas Soome get_slice(ib_data_t *data, struct partlist *pl, struct dk_gpt *vtoc,
1069d7802caeSToomas Soome     uint16_t tag)
10700c946d80SToomas Soome {
1071d7802caeSToomas Soome 	uint_t i;
1072d7802caeSToomas Soome 	ib_device_t *device = pl->pl_device;
1073d7802caeSToomas Soome 	char *path, *ptr;
10740c946d80SToomas Soome 
1075d7802caeSToomas Soome 	if (tag != V_BOOT && tag != V_SYSTEM)
1076d7802caeSToomas Soome 		return (false);
10770c946d80SToomas Soome 
1078d7802caeSToomas Soome 	for (i = 0; i < vtoc->efi_nparts; i++) {
1079d7802caeSToomas Soome 		if (vtoc->efi_parts[i].p_tag == tag) {
1080d7802caeSToomas Soome 			if ((path = strdup(data->target.path)) == NULL) {
1081d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1082d7802caeSToomas Soome 				return (false);
1083d7802caeSToomas Soome 			}
1084d7802caeSToomas Soome 			ptr = strrchr(path, 's');
1085d7802caeSToomas Soome 			ptr++;
1086d7802caeSToomas Soome 			*ptr = '\0';
1087d7802caeSToomas Soome 			(void) asprintf(&ptr, "%s%d", path, i);
1088d7802caeSToomas Soome 			free(path);
1089d7802caeSToomas Soome 			if (ptr == NULL) {
1090d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1091d7802caeSToomas Soome 				return (false);
1092d7802caeSToomas Soome 			}
1093d7802caeSToomas Soome 			pl->pl_devname = ptr;
1094d7802caeSToomas Soome 			device->stage.id = i;
1095d7802caeSToomas Soome 			device->stage.devtype = IB_DEV_EFI;
1096d7802caeSToomas Soome 			switch (vtoc->efi_parts[i].p_tag) {
1097d7802caeSToomas Soome 			case V_BOOT:
1098d7802caeSToomas Soome 				device->stage.fstype = IB_FS_NONE;
1099d7802caeSToomas Soome 				/* leave sector 0 for VBR */
1100d7802caeSToomas Soome 				device->stage.offset = 1;
1101d7802caeSToomas Soome 				break;
1102d7802caeSToomas Soome 			case V_SYSTEM:
1103d7802caeSToomas Soome 				device->stage.fstype = IB_FS_PCFS;
1104d7802caeSToomas Soome 				break;
1105d7802caeSToomas Soome 			}
1106d7802caeSToomas Soome 			device->stage.tag = vtoc->efi_parts[i].p_tag;
1107d7802caeSToomas Soome 			device->stage.start = vtoc->efi_parts[i].p_start;
1108d7802caeSToomas Soome 			device->stage.size = vtoc->efi_parts[i].p_size;
1109d7802caeSToomas Soome 			break;
11100c946d80SToomas Soome 		}
11110c946d80SToomas Soome 	}
1112d7802caeSToomas Soome 	return (true);
1113d7802caeSToomas Soome }
11140c946d80SToomas Soome 
1115d7802caeSToomas Soome static bool
allocate_slice(ib_data_t * data,struct dk_gpt * vtoc,uint16_t tag,struct partlist ** plp)1116d7802caeSToomas Soome allocate_slice(ib_data_t *data, struct dk_gpt *vtoc, uint16_t tag,
1117d7802caeSToomas Soome     struct partlist **plp)
1118d7802caeSToomas Soome {
1119d7802caeSToomas Soome 	struct partlist *pl;
11200c946d80SToomas Soome 
1121d7802caeSToomas Soome 	*plp = NULL;
1122d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1123d7802caeSToomas Soome 		return (false);
11240c946d80SToomas Soome 
1125d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
1126d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
1127d7802caeSToomas Soome 		perror("calloc");
1128d7802caeSToomas Soome 		partlist_free(pl);
1129d7802caeSToomas Soome 		return (false);
1130d7802caeSToomas Soome 	}
1131d7802caeSToomas Soome 	if (!get_slice(data, pl, vtoc, tag)) {
1132d7802caeSToomas Soome 		partlist_free(pl);
1133d7802caeSToomas Soome 		return (false);
11340c946d80SToomas Soome 	}
11350c946d80SToomas Soome 
1136d7802caeSToomas Soome 	/* tag was not found */
1137d7802caeSToomas Soome 	if (pl->pl_devname == NULL)
1138d7802caeSToomas Soome 		partlist_free(pl);
1139d7802caeSToomas Soome 	else
1140d7802caeSToomas Soome 		*plp = pl;
1141d7802caeSToomas Soome 
1142d7802caeSToomas Soome 	return (true);
11430c946d80SToomas Soome }
11440c946d80SToomas Soome 
1145d7802caeSToomas Soome static bool
probe_gpt(ib_data_t * data)1146d7802caeSToomas Soome probe_gpt(ib_data_t *data)
11470c946d80SToomas Soome {
1148d7802caeSToomas Soome 	struct partlist *pl;
1149d7802caeSToomas Soome 	struct dk_gpt *vtoc;
1150d7802caeSToomas Soome 	ib_device_t *device;
1151d7802caeSToomas Soome 	int slice, fd;
1152d7802caeSToomas Soome 	bool rv = false;
11530c946d80SToomas Soome 
1154d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1155d7802caeSToomas Soome 		return (rv);
11560c946d80SToomas Soome 
1157d7802caeSToomas Soome 	slice = efi_alloc_and_read(fd, &vtoc);
1158d7802caeSToomas Soome 	(void) close(fd);
1159d7802caeSToomas Soome 	if (slice < 0)
1160d7802caeSToomas Soome 		return (rv);
1161d7802caeSToomas Soome 
1162d7802caeSToomas Soome 	data->device.devtype = IB_DEV_EFI;
1163d7802caeSToomas Soome 	data->target.start = vtoc->efi_parts[slice].p_start;
1164d7802caeSToomas Soome 	data->target.size = vtoc->efi_parts[slice].p_size;
11650c946d80SToomas Soome 
1166d7802caeSToomas Soome 	/* Always update PMBR. */
1167d8d05a42SToomas Soome 	force_mbr = true;
1168d8d05a42SToomas Soome 	write_mbr = true;
1169d7802caeSToomas Soome 
1170d7802caeSToomas Soome 	/*
1171d7802caeSToomas Soome 	 * With GPT we can have boot partition and ESP.
1172d7802caeSToomas Soome 	 * Boot partition can have both stage 1 and stage 2.
1173d7802caeSToomas Soome 	 */
1174d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1175d7802caeSToomas Soome 		goto done;
1176d7802caeSToomas Soome 	if (pl != NULL) {
1177d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1178d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1179d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1180d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1181d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1182d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1183d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1184d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1185d7802caeSToomas Soome 	} else if (data->target.fstype != IB_FS_ZFS) {
1186d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Booting %s from EFI "
1187d7802caeSToomas Soome 		    "labeled disks requires the boot partition.\n"),
1188d7802caeSToomas Soome 		    data->target.fstype == IB_FS_UFS?
1189d7802caeSToomas Soome 		    MNTTYPE_UFS : MNTTYPE_PCFS);
1190d7802caeSToomas Soome 		goto done;
1191d7802caeSToomas Soome 	}
1192d7802caeSToomas Soome 	/* Add stage 2 */
1193d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1194d7802caeSToomas Soome 		goto done;
1195d7802caeSToomas Soome 	if (pl != NULL) {
1196d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1197d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1198d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1199d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1200d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1201d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1202d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1203d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1204d7802caeSToomas Soome 	}
1205d7802caeSToomas Soome 
1206d7802caeSToomas Soome 	/* ESP can have 32- and 64-bit boot code. */
1207d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1208d7802caeSToomas Soome 		goto done;
1209d7802caeSToomas Soome 	if (pl != NULL) {
1210d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTIA32;
1211d7802caeSToomas Soome 		pl->pl_src_name = efi32;
1212d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1213d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1214d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1215d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1216d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1217d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1218d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1219d7802caeSToomas Soome 	}
1220d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1221d7802caeSToomas Soome 		goto done;
1222d7802caeSToomas Soome 	if (pl != NULL) {
1223d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTX64;
1224d7802caeSToomas Soome 		pl->pl_src_name = efi64;
1225d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1226d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1227d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1228d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1229d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1230d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1231d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1232d7802caeSToomas Soome 	}
1233d7802caeSToomas Soome 
1234d7802caeSToomas Soome 	/* add stage for our target file system slice */
1235d7802caeSToomas Soome 	pl = partlist_alloc();
1236d7802caeSToomas Soome 	if (pl == NULL)
1237d7802caeSToomas Soome 		goto done;
1238d7802caeSToomas Soome 
1239d7802caeSToomas Soome 	device = pl->pl_device;
1240d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1241d7802caeSToomas Soome 	if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1242d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1243d7802caeSToomas Soome 		partlist_free(pl);
1244d7802caeSToomas Soome 		goto done;
12450c946d80SToomas Soome 	}
12460c946d80SToomas Soome 
1247d7802caeSToomas Soome 	device->stage.id = slice;
1248d7802caeSToomas Soome 	device->stage.start = vtoc->efi_parts[slice].p_start;
1249d7802caeSToomas Soome 	device->stage.size = vtoc->efi_parts[slice].p_size;
12500c946d80SToomas Soome 
1251d7802caeSToomas Soome 	/* ZFS and UFS can have stage1 in boot area. */
1252d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS ||
1253d7802caeSToomas Soome 	    data->target.fstype == IB_FS_UFS) {
1254d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1255d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1256d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1257d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1258d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1259d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1260d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1261d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1262d7802caeSToomas Soome 	}
12630c946d80SToomas Soome 
1264d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS) {
1265d7802caeSToomas Soome 		pl = partlist_alloc();
1266d7802caeSToomas Soome 		if (pl == NULL)
1267d7802caeSToomas Soome 			goto done;
12680c946d80SToomas Soome 
1269d7802caeSToomas Soome 		device = pl->pl_device;
1270d7802caeSToomas Soome 		device->stage.devtype = data->device.devtype;
1271d7802caeSToomas Soome 
1272d7802caeSToomas Soome 		if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1273d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1274d7802caeSToomas Soome 			goto done;
12750c946d80SToomas Soome 		}
12760c946d80SToomas Soome 
1277d7802caeSToomas Soome 		device->stage.id = slice;
1278d7802caeSToomas Soome 		device->stage.start = vtoc->efi_parts[slice].p_start;
1279d7802caeSToomas Soome 		device->stage.size = vtoc->efi_parts[slice].p_size;
1280d7802caeSToomas Soome 
1281d7802caeSToomas Soome 		device->stage.offset = BBLK_ZFS_BLK_OFF;
1282d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1283d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1284d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1285d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1286d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1287d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1288d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1289d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1290d7802caeSToomas Soome 	}
1291d7802caeSToomas Soome 	rv = true;
1292d7802caeSToomas Soome done:
1293d7802caeSToomas Soome 	efi_free(vtoc);
1294d7802caeSToomas Soome 	return (rv);
1295d7802caeSToomas Soome }
12960c946d80SToomas Soome 
1297d7802caeSToomas Soome static bool
get_start_sector(ib_data_t * data,struct extpartition * v_part,diskaddr_t * start)1298d7802caeSToomas Soome get_start_sector(ib_data_t *data, struct extpartition *v_part,
1299d7802caeSToomas Soome     diskaddr_t *start)
1300d7802caeSToomas Soome {
1301d7802caeSToomas Soome 	struct partlist *pl;
1302d7802caeSToomas Soome 	struct mboot *mbr;
1303d7802caeSToomas Soome 	struct ipart *part;
1304d7802caeSToomas Soome 	struct part_info dkpi;
1305d7802caeSToomas Soome 	struct extpart_info edkpi;
1306d7802caeSToomas Soome 	uint32_t secnum, numsec;
1307d7802caeSToomas Soome 	ext_part_t *epp;
1308d7802caeSToomas Soome 	ushort_t i;
1309d7802caeSToomas Soome 	int fd, rval, pno;
1310d7802caeSToomas Soome 
1311d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1312d7802caeSToomas Soome 		return (false);
1313d7802caeSToomas Soome 
1314d7802caeSToomas Soome 	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
1315d7802caeSToomas Soome 		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
13160c946d80SToomas Soome 			(void) fprintf(stderr, gettext("cannot get the "
13170c946d80SToomas Soome 			    "slice information of the disk\n"));
1318d7802caeSToomas Soome 			(void) close(fd);
1319d7802caeSToomas Soome 			return (false);
13200c946d80SToomas Soome 		} else {
13210c946d80SToomas Soome 			edkpi.p_start = dkpi.p_start;
13220c946d80SToomas Soome 			edkpi.p_length = dkpi.p_length;
13230c946d80SToomas Soome 		}
13240c946d80SToomas Soome 	}
1325d7802caeSToomas Soome 	(void) close(fd);
13260c946d80SToomas Soome 
1327d7802caeSToomas Soome 	/* Set target file system start and size */
1328d7802caeSToomas Soome 	data->target.start = edkpi.p_start;
1329d7802caeSToomas Soome 	data->target.size = edkpi.p_length;
13300c946d80SToomas Soome 
1331d7802caeSToomas Soome 	/* This is our MBR partition start. */
1332d7802caeSToomas Soome 	edkpi.p_start -= v_part->p_start;
13330c946d80SToomas Soome 
1334d7802caeSToomas Soome 	/* Head is always MBR */
1335d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1336d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1337d7802caeSToomas Soome 		return (false);
13380c946d80SToomas Soome 
1339d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1340d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
13410c946d80SToomas Soome 
1342d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1343d7802caeSToomas Soome 		if (part[i].relsect == edkpi.p_start) {
1344d7802caeSToomas Soome 			*start = part[i].relsect;
1345d7802caeSToomas Soome 			return (true);
1346d7802caeSToomas Soome 		}
13470c946d80SToomas Soome 	}
13480c946d80SToomas Soome 
1349d7802caeSToomas Soome 	rval = libfdisk_init(&epp, pl->pl_devname, part, FDISK_READ_DISK);
1350d7802caeSToomas Soome 	if (rval != FDISK_SUCCESS) {
13510c946d80SToomas Soome 		switch (rval) {
13520c946d80SToomas Soome 			/*
13530c946d80SToomas Soome 			 * The first 3 cases are not an error per-se, just that
13540c946d80SToomas Soome 			 * there is no Solaris logical partition
13550c946d80SToomas Soome 			 */
13560c946d80SToomas Soome 			case FDISK_EBADLOGDRIVE:
13570c946d80SToomas Soome 			case FDISK_ENOLOGDRIVE:
13580c946d80SToomas Soome 			case FDISK_EBADMAGIC:
13590c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Solaris "
13600c946d80SToomas Soome 				    "partition not found. "
1361d7802caeSToomas Soome 				    "Aborting operation. %d\n"), rval);
1362d7802caeSToomas Soome 				return (false);
13630c946d80SToomas Soome 			case FDISK_ENOVGEOM:
13640c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13650c946d80SToomas Soome 				    "virtual geometry\n"));
1366d7802caeSToomas Soome 				return (false);
13670c946d80SToomas Soome 			case FDISK_ENOPGEOM:
13680c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13690c946d80SToomas Soome 				    "physical geometry\n"));
1370d7802caeSToomas Soome 				return (false);
13710c946d80SToomas Soome 			case FDISK_ENOLGEOM:
13720c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
13730c946d80SToomas Soome 				    "label geometry\n"));
1374d7802caeSToomas Soome 				return (false);
13750c946d80SToomas Soome 			default:
13760c946d80SToomas Soome 				(void) fprintf(stderr, gettext("Failed to "
13770c946d80SToomas Soome 				    "initialize libfdisk.\n"));
1378d7802caeSToomas Soome 				return (false);
13790c946d80SToomas Soome 		}
13800c946d80SToomas Soome 	}
13810c946d80SToomas Soome 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
13820c946d80SToomas Soome 	libfdisk_fini(&epp);
13830c946d80SToomas Soome 	if (rval != FDISK_SUCCESS) {
13840c946d80SToomas Soome 		/* No solaris logical partition */
13850c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Solaris partition not found. "
13860c946d80SToomas Soome 		    "Aborting operation.\n"));
1387d7802caeSToomas Soome 		return (false);
13880c946d80SToomas Soome 	}
1389d7802caeSToomas Soome 	*start = secnum;
1390d7802caeSToomas Soome 	return (true);
1391d7802caeSToomas Soome }
13920c946d80SToomas Soome 
1393d7802caeSToomas Soome /*
1394d7802caeSToomas Soome  * On x86 the VTOC table is inside MBR partition and to get
1395d7802caeSToomas Soome  * absolute sectors, we need to add MBR partition start to VTOC slice start.
1396d7802caeSToomas Soome  */
1397d7802caeSToomas Soome static bool
probe_vtoc(ib_data_t * data)1398d7802caeSToomas Soome probe_vtoc(ib_data_t *data)
1399d7802caeSToomas Soome {
1400d7802caeSToomas Soome 	struct partlist *pl;
1401d7802caeSToomas Soome 	struct extvtoc exvtoc;
1402d7802caeSToomas Soome 	ib_device_t *device;
1403d7802caeSToomas Soome 	char *path, *ptr;
1404d7802caeSToomas Soome 	ushort_t i;
1405d7802caeSToomas Soome 	int slice, fd;
1406d7802caeSToomas Soome 	diskaddr_t start;
1407d7802caeSToomas Soome 	bool rv;
14080c946d80SToomas Soome 
1409d7802caeSToomas Soome 	rv = false;
1410d7802caeSToomas Soome 
1411d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1412d7802caeSToomas Soome 		return (rv);
1413d7802caeSToomas Soome 
1414d7802caeSToomas Soome 	slice = read_extvtoc(fd, &exvtoc);
1415d7802caeSToomas Soome 	(void) close(fd);
1416d7802caeSToomas Soome 	if (slice < 0)
1417d7802caeSToomas Soome 		return (rv);
1418d7802caeSToomas Soome 	data->device.devtype = IB_DEV_VTOC;
1419d7802caeSToomas Soome 
1420d7802caeSToomas Soome 	if (!get_start_sector(data, exvtoc.v_part + slice, &start))
1421d7802caeSToomas Soome 		return (rv);
1422d7802caeSToomas Soome 
1423d7802caeSToomas Soome 	if (exvtoc.v_part[slice].p_tag == V_BACKUP) {
1424d7802caeSToomas Soome 		/*
1425d7802caeSToomas Soome 		 * NOTE: we could relax there and allow zfs boot on
1426d7802caeSToomas Soome 		 * slice 2, but lets keep traditional limits.
1427d7802caeSToomas Soome 		 */
1428d7802caeSToomas Soome 		(void) fprintf(stderr, gettext(
1429d7802caeSToomas Soome 		    "raw device must be a root slice (not backup)\n"));
1430d7802caeSToomas Soome 		return (rv);
1431d7802caeSToomas Soome 	}
1432d7802caeSToomas Soome 
1433d7802caeSToomas Soome 	if ((path = strdup(data->target.path)) == NULL) {
1434d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1435d7802caeSToomas Soome 		return (false);
1436d7802caeSToomas Soome 	}
1437d7802caeSToomas Soome 
1438d7802caeSToomas Soome 	data->target.start = start + exvtoc.v_part[slice].p_start;
1439d7802caeSToomas Soome 	data->target.size = exvtoc.v_part[slice].p_size;
1440d7802caeSToomas Soome 
1441d7802caeSToomas Soome 	/* Search for boot slice. */
1442d7802caeSToomas Soome 	for (i = 0; i < exvtoc.v_nparts; i++) {
1443d7802caeSToomas Soome 		if (exvtoc.v_part[i].p_tag == V_BOOT)
1444d7802caeSToomas Soome 			break;
1445d7802caeSToomas Soome 	}
1446d7802caeSToomas Soome 
1447d7802caeSToomas Soome 	if (i == exvtoc.v_nparts ||
1448d7802caeSToomas Soome 	    exvtoc.v_part[i].p_size == 0) {
1449d7802caeSToomas Soome 		/* fall back to slice V_BACKUP */
1450d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1451d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_tag == V_BACKUP)
1452d7802caeSToomas Soome 				break;
1453d7802caeSToomas Soome 		}
1454d7802caeSToomas Soome 		/* Still nothing? Error out. */
1455d7802caeSToomas Soome 		if (i == exvtoc.v_nparts ||
1456d7802caeSToomas Soome 		    exvtoc.v_part[i].p_size == 0) {
1457d7802caeSToomas Soome 			free(path);
1458d7802caeSToomas Soome 			return (false);
1459d7802caeSToomas Soome 		}
1460d7802caeSToomas Soome 	}
1461d7802caeSToomas Soome 
1462d7802caeSToomas Soome 	/* Create path. */
1463d7802caeSToomas Soome 	ptr = strrchr(path, 's');
1464d7802caeSToomas Soome 	ptr++;
1465d7802caeSToomas Soome 	*ptr = '\0';
1466d7802caeSToomas Soome 	(void) asprintf(&ptr, "%s%d", path, i);
1467d7802caeSToomas Soome 	free(path);
1468d7802caeSToomas Soome 	if (ptr == NULL) {
1469d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1470d7802caeSToomas Soome 		return (false);
1471d7802caeSToomas Soome 	}
1472d7802caeSToomas Soome 
1473d7802caeSToomas Soome 	pl = partlist_alloc();
1474d7802caeSToomas Soome 	if (pl == NULL) {
1475d7802caeSToomas Soome 		free(ptr);
1476d7802caeSToomas Soome 		return (false);
1477d7802caeSToomas Soome 	}
1478d7802caeSToomas Soome 	pl->pl_devname = ptr;
1479d7802caeSToomas Soome 	device = pl->pl_device;
1480d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1481d7802caeSToomas Soome 	device->stage.id = i;
1482d7802caeSToomas Soome 	device->stage.tag = exvtoc.v_part[i].p_tag;
1483d7802caeSToomas Soome 	device->stage.start = start + exvtoc.v_part[i].p_start;
1484d7802caeSToomas Soome 	device->stage.size = exvtoc.v_part[i].p_size;
1485d7802caeSToomas Soome 
1486d7802caeSToomas Soome 	/* Fix size if this slice is in fact V_BACKUP */
1487d7802caeSToomas Soome 	if (exvtoc.v_part[i].p_tag == V_BACKUP) {
1488d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1489d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start == 0)
1490d7802caeSToomas Soome 				continue;
1491d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_size == 0)
1492d7802caeSToomas Soome 				continue;
1493d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start <
1494d7802caeSToomas Soome 			    device->stage.size)
1495d7802caeSToomas Soome 				device->stage.size =
1496d7802caeSToomas Soome 				    exvtoc.v_part[i].p_start;
14970c946d80SToomas Soome 		}
14980c946d80SToomas Soome 	}
14990c946d80SToomas Soome 
1500d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1501d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1502d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1503d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1504d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1505d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1506d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1507d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1508d7802caeSToomas Soome 
1509d7802caeSToomas Soome 	/* Create instance for stage 2 */
1510d7802caeSToomas Soome 	pl = partlist_alloc();
1511d7802caeSToomas Soome 	if (pl == NULL) {
1512d7802caeSToomas Soome 		free(ptr);
1513d7802caeSToomas Soome 		return (false);
1514d7802caeSToomas Soome 	}
1515d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1516d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1517d7802caeSToomas Soome 		partlist_free(pl);
1518d7802caeSToomas Soome 		return (false);
1519d7802caeSToomas Soome 	}
1520d7802caeSToomas Soome 	pl->pl_device->stage.devtype = data->device.devtype;
1521d7802caeSToomas Soome 	pl->pl_device->stage.id = device->stage.id;
1522d7802caeSToomas Soome 	pl->pl_device->stage.offset = BBLK_BLKLIST_OFF;
1523d7802caeSToomas Soome 	pl->pl_device->stage.tag = device->stage.tag;
1524d7802caeSToomas Soome 	pl->pl_device->stage.start = device->stage.start;
1525d7802caeSToomas Soome 	pl->pl_device->stage.size = device->stage.size;
1526d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1527d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1528d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1529d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1530d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1531d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1532d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1533d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1534d7802caeSToomas Soome 
1535d7802caeSToomas Soome 	/* And we are done. */
1536d7802caeSToomas Soome 	rv = true;
1537d7802caeSToomas Soome 	return (rv);
1538d7802caeSToomas Soome }
1539d7802caeSToomas Soome 
1540d7802caeSToomas Soome static bool
probe_mbr(ib_data_t * data)1541d7802caeSToomas Soome probe_mbr(ib_data_t *data)
1542d7802caeSToomas Soome {
1543d7802caeSToomas Soome 	struct partlist *pl;
1544d7802caeSToomas Soome 	struct ipart *part;
1545d7802caeSToomas Soome 	struct mboot *mbr;
1546d7802caeSToomas Soome 	ib_device_t *device;
1547d7802caeSToomas Soome 	char *path, *ptr;
1548d7802caeSToomas Soome 	int i, rv;
1549d7802caeSToomas Soome 
1550d7802caeSToomas Soome 	data->device.devtype = IB_DEV_MBR;
1551d7802caeSToomas Soome 
1552d7802caeSToomas Soome 	/* Head is always MBR */
1553d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1554d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1555d7802caeSToomas Soome 		return (false);
1556d7802caeSToomas Soome 
1557d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1558d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
1559d7802caeSToomas Soome 
1560d7802caeSToomas Soome 	/* Set target file system start and size */
1561d7802caeSToomas Soome 	data->target.start = part[data->target.id - 1].relsect;
1562d7802caeSToomas Soome 	data->target.size = part[data->target.id - 1].numsect;
1563d7802caeSToomas Soome 
1564d7802caeSToomas Soome 	/* Use X86BOOT partition if we have one. */
1565d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1566d7802caeSToomas Soome 		if (part[i].systid == X86BOOT)
1567d7802caeSToomas Soome 			break;
1568d7802caeSToomas Soome 	}
1569d7802caeSToomas Soome 
1570d7802caeSToomas Soome 	/* Keep device name of whole disk device. */
1571d7802caeSToomas Soome 	path = (char *)pl->pl_devname;
1572d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1573d7802caeSToomas Soome 		return (false);
1574d7802caeSToomas Soome 	device = pl->pl_device;
1575d7802caeSToomas Soome 
15760c946d80SToomas Soome 	/*
1577d7802caeSToomas Soome 	 * No X86BOOT, try to use space between MBR and first
1578d7802caeSToomas Soome 	 * partition.
15790c946d80SToomas Soome 	 */
1580d7802caeSToomas Soome 	if (i == FD_NUMPART) {
1581d7802caeSToomas Soome 		pl->pl_devname = strdup(path);
1582d7802caeSToomas Soome 		if (pl->pl_devname == NULL) {
1583d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1584d7802caeSToomas Soome 			partlist_free(pl);
1585d7802caeSToomas Soome 			return (false);
1586d7802caeSToomas Soome 		}
1587d7802caeSToomas Soome 		device->stage.id = 0;
1588d7802caeSToomas Soome 		device->stage.devtype = IB_DEV_MBR;
1589d7802caeSToomas Soome 		device->stage.fstype = IB_FS_NONE;
1590d7802caeSToomas Soome 		device->stage.start = 0;
1591d7802caeSToomas Soome 		device->stage.size = part[0].relsect;
1592d7802caeSToomas Soome 		device->stage.offset = BBLK_BLKLIST_OFF;
1593d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1594d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1595d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1596d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1597d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1598d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1599d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1600d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1601d7802caeSToomas Soome 
1602d7802caeSToomas Soome 		/* We have MBR for stage1 and gap for stage2, we are done. */
1603d7802caeSToomas Soome 		return (true);
16040c946d80SToomas Soome 	}
16050c946d80SToomas Soome 
1606d7802caeSToomas Soome 	if ((path = strdup(path)) == NULL) {
1607d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1608d7802caeSToomas Soome 		partlist_free(pl);
1609d7802caeSToomas Soome 		return (false);
1610d7802caeSToomas Soome 	}
1611d7802caeSToomas Soome 	ptr = strrchr(path, 'p');
1612d7802caeSToomas Soome 	ptr++;
1613d7802caeSToomas Soome 	*ptr = '\0';
1614d7802caeSToomas Soome 	/* partitions are p1..p4 */
1615d7802caeSToomas Soome 	rv = asprintf(&ptr, "%s%d", path, i + 1);
1616d7802caeSToomas Soome 	free(path);
1617d7802caeSToomas Soome 	if (rv < 0) {
1618d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1619d7802caeSToomas Soome 		partlist_free(pl);
1620d7802caeSToomas Soome 		return (false);
1621d7802caeSToomas Soome 	}
1622d7802caeSToomas Soome 	pl->pl_devname = ptr;
1623d7802caeSToomas Soome 	device->stage.id = i + 1;
1624d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1625d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1626d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1627d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1628d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1629d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1630d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1631d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1632d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1633d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1634d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1635d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1636d7802caeSToomas Soome 
1637d7802caeSToomas Soome 	pl = partlist_alloc();
1638d7802caeSToomas Soome 	if (pl == NULL)
1639d7802caeSToomas Soome 		return (false);
1640d7802caeSToomas Soome 	device = pl->pl_device;
1641d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1642d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1643d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1644d7802caeSToomas Soome 		partlist_free(pl);
1645d7802caeSToomas Soome 		return (false);
1646d7802caeSToomas Soome 	}
1647d7802caeSToomas Soome 	device->stage.id = i + 1;
1648d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1649d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1650d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1651d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1652d7802caeSToomas Soome 	device->stage.offset = 1;
1653d7802caeSToomas Soome 	/* This is boot partition */
1654d7802caeSToomas Soome 	device->stage.tag = V_BOOT;
1655d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1656d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1657d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1658d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1659d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1660d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1661d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1662d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1663d7802caeSToomas Soome 
1664d7802caeSToomas Soome 	return (true);
1665d7802caeSToomas Soome }
1666d7802caeSToomas Soome 
1667d7802caeSToomas Soome static bool
probe_device(ib_data_t * data,const char * dev)1668d7802caeSToomas Soome probe_device(ib_data_t *data, const char *dev)
1669d7802caeSToomas Soome {
1670d7802caeSToomas Soome 	struct partlist *pl;
1671d7802caeSToomas Soome 	struct stat sb;
1672d7802caeSToomas Soome 	const char *ptr;
1673d7802caeSToomas Soome 	char *p0;
1674d7802caeSToomas Soome 	int fd, len;
1675d7802caeSToomas Soome 
1676d7802caeSToomas Soome 	if (dev == NULL)
1677d7802caeSToomas Soome 		return (NULL);
1678d7802caeSToomas Soome 
1679d7802caeSToomas Soome 	len = strlen(dev);
1680d7802caeSToomas Soome 
1681d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1682d7802caeSToomas Soome 		return (false);
1683d7802caeSToomas Soome 
1684d7802caeSToomas Soome 	if (stat(dev, &sb) == -1) {
1685d7802caeSToomas Soome 		perror("stat");
1686d7802caeSToomas Soome 		partlist_free(pl);
1687d7802caeSToomas Soome 		return (false);
1688d7802caeSToomas Soome 	}
1689d7802caeSToomas Soome 
1690d7802caeSToomas Soome 	/* We have regular file, register it and we are done. */
1691d7802caeSToomas Soome 	if (S_ISREG(sb.st_mode) != 0) {
1692d7802caeSToomas Soome 		pl->pl_devname = (char *)dev;
1693d7802caeSToomas Soome 
1694d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_FILE;
1695d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_file_cb;
1696d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1697d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1698d7802caeSToomas Soome 		return (true);
1699d7802caeSToomas Soome 	}
1700d7802caeSToomas Soome 
1701d7802caeSToomas Soome 	/*
1702d7802caeSToomas Soome 	 * This is block device.
1703d7802caeSToomas Soome 	 * We do not allow to specify whole disk device (cXtYdZp0 or cXtYdZ).
1704d7802caeSToomas Soome 	 */
1705d7802caeSToomas Soome 	if ((ptr = strrchr(dev, '/')) == NULL)
1706d7802caeSToomas Soome 		ptr = dev;
1707d7802caeSToomas Soome 	if ((strrchr(ptr, 'p') == NULL && strrchr(ptr, 's') == NULL) ||
1708d7802caeSToomas Soome 	    (dev[len - 2] == 'p' && dev[len - 1] == '0')) {
1709d7802caeSToomas Soome 		(void) fprintf(stderr,
1710d7802caeSToomas Soome 		    gettext("whole disk device is not supported\n"));
1711d7802caeSToomas Soome 		partlist_free(pl);
1712d7802caeSToomas Soome 		return (false);
1713d7802caeSToomas Soome 	}
1714d7802caeSToomas Soome 
1715d7802caeSToomas Soome 	data->target.path = (char *)dev;
1716d7802caeSToomas Soome 	if (!probe_fstyp(data)) {
1717d7802caeSToomas Soome 		partlist_free(pl);
1718d7802caeSToomas Soome 		return (false);
1719d7802caeSToomas Soome 	}
1720d7802caeSToomas Soome 
1721d7802caeSToomas Soome 	/* We start from identifying the whole disk. */
1722d7802caeSToomas Soome 	if ((p0 = strdup(dev)) == NULL) {
1723d7802caeSToomas Soome 		perror("calloc");
1724d7802caeSToomas Soome 		partlist_free(pl);
1725d7802caeSToomas Soome 		return (false);
1726d7802caeSToomas Soome 	}
1727d7802caeSToomas Soome 
1728d7802caeSToomas Soome 	pl->pl_devname = p0;
1729d7802caeSToomas Soome 	/* Change device name to p0 */
1730d7802caeSToomas Soome 	if ((ptr = strrchr(p0, 'p')) == NULL)
1731d7802caeSToomas Soome 		ptr = strrchr(p0, 's');
1732d7802caeSToomas Soome 	p0 = (char *)ptr;
1733d7802caeSToomas Soome 	p0[0] = 'p';
1734d7802caeSToomas Soome 	p0[1] = '0';
1735d7802caeSToomas Soome 	p0[2] = '\0';
1736d7802caeSToomas Soome 
1737d7802caeSToomas Soome 	if ((fd = open_device(pl->pl_devname)) == -1) {
1738d7802caeSToomas Soome 		partlist_free(pl);
1739d7802caeSToomas Soome 		return (false);
1740d7802caeSToomas Soome 	}
1741d7802caeSToomas Soome 
1742d7802caeSToomas Soome 	sector_size = get_media_info(fd);
1743d7802caeSToomas Soome 	(void) close(fd);
1744d7802caeSToomas Soome 
1745d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1746d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_MBR;
1747d7802caeSToomas Soome 	pl->pl_cb.compare = compare_mbr_cb;
1748d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1749d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1750d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1751d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1752d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1753d7802caeSToomas Soome 
1754d7802caeSToomas Soome 	if (probe_gpt(data))
1755d7802caeSToomas Soome 		return (true);
1756d7802caeSToomas Soome 
1757d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1758d7802caeSToomas Soome 		if (probe_vtoc(data))
1759d7802caeSToomas Soome 			return (true);
1760d7802caeSToomas Soome 
1761d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1762d7802caeSToomas Soome 		return (probe_mbr(data));
1763d7802caeSToomas Soome 
1764d7802caeSToomas Soome 	return (false);
1765d7802caeSToomas Soome }
1766d7802caeSToomas Soome 
1767d7802caeSToomas Soome static int
read_bootblock_from_file(const char * file,ib_bootblock_t * bblock)1768d7802caeSToomas Soome read_bootblock_from_file(const char *file, ib_bootblock_t *bblock)
1769d7802caeSToomas Soome {
1770d7802caeSToomas Soome 	struct stat	sb;
1771d7802caeSToomas Soome 	uint32_t	buf_size;
1772d7802caeSToomas Soome 	uint32_t	mboot_off;
1773d7802caeSToomas Soome 	int		fd = -1;
1774d7802caeSToomas Soome 	int		retval = BC_ERROR;
1775d7802caeSToomas Soome 
1776d7802caeSToomas Soome 	assert(bblock != NULL);
1777d7802caeSToomas Soome 	assert(file != NULL);
1778d7802caeSToomas Soome 
1779d7802caeSToomas Soome 	fd = open(file, O_RDONLY);
1780d7802caeSToomas Soome 	if (fd == -1) {
1781d7802caeSToomas Soome 		BOOT_DEBUG("Error opening %s\n", file);
1782d7802caeSToomas Soome 		goto out;
1783d7802caeSToomas Soome 	}
1784d7802caeSToomas Soome 
1785d7802caeSToomas Soome 	if (fstat(fd, &sb) == -1) {
1786d7802caeSToomas Soome 		BOOT_DEBUG("Error getting information (stat) about %s", file);
1787d7802caeSToomas Soome 		perror("stat");
1788d7802caeSToomas Soome 		goto outfd;
1789d7802caeSToomas Soome 	}
1790d7802caeSToomas Soome 
1791d7802caeSToomas Soome 	/* loader bootblock has version built in */
1792d7802caeSToomas Soome 	buf_size = sb.st_size;
179346d70dceSToomas Soome 	if (buf_size == 0)
179446d70dceSToomas Soome 		goto outfd;
1795d7802caeSToomas Soome 
1796d7802caeSToomas Soome 	bblock->buf_size = buf_size;
1797d7802caeSToomas Soome 	BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
1798d7802caeSToomas Soome 	    bblock->buf_size);
1799d7802caeSToomas Soome 
1800d7802caeSToomas Soome 	bblock->buf = malloc(buf_size);
1801d7802caeSToomas Soome 	if (bblock->buf == NULL) {
1802d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1803d7802caeSToomas Soome 		goto outbuf;
1804d7802caeSToomas Soome 	}
1805d7802caeSToomas Soome 	bblock->file = bblock->buf;
1806d7802caeSToomas Soome 
1807d7802caeSToomas Soome 	if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) {
1808d7802caeSToomas Soome 		BOOT_DEBUG("Read from %s failed\n", file);
1809d7802caeSToomas Soome 		perror("read");
1810d7802caeSToomas Soome 		goto outfd;
1811d7802caeSToomas Soome 	}
1812d7802caeSToomas Soome 
1813d7802caeSToomas Soome 	buf_size = MIN(buf_size, MBOOT_SCAN_SIZE);
1814d7802caeSToomas Soome 	if (find_multiboot(bblock->file, buf_size, &mboot_off)
1815d7802caeSToomas Soome 	    != BC_SUCCESS) {
1816d7802caeSToomas Soome 		(void) fprintf(stderr,
1817d7802caeSToomas Soome 		    gettext("Unable to find multiboot header\n"));
1818d7802caeSToomas Soome 		goto outfd;
1819d7802caeSToomas Soome 	}
1820d7802caeSToomas Soome 
1821d7802caeSToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
1822d7802caeSToomas Soome 	bblock->mboot_off = mboot_off;
1823d7802caeSToomas Soome 
1824d7802caeSToomas Soome 	bblock->file_size =
1825d7802caeSToomas Soome 	    bblock->mboot->load_end_addr - bblock->mboot->load_addr;
1826d7802caeSToomas Soome 	BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
1827d7802caeSToomas Soome 
1828d7802caeSToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
1829d7802caeSToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1830d7802caeSToomas Soome 
1831d7802caeSToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
1832d7802caeSToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
1833d7802caeSToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
1834d7802caeSToomas Soome 
1835d7802caeSToomas Soome 	(void) close(fd);
18360c946d80SToomas Soome 	return (BC_SUCCESS);
1837d7802caeSToomas Soome 
1838d7802caeSToomas Soome outbuf:
1839d7802caeSToomas Soome 	(void) free(bblock->buf);
1840d7802caeSToomas Soome 	bblock->buf = NULL;
1841d7802caeSToomas Soome outfd:
1842d7802caeSToomas Soome 	(void) close(fd);
1843d7802caeSToomas Soome out:
1844d7802caeSToomas Soome 	if (retval == BC_ERROR) {
1845d7802caeSToomas Soome 		(void) fprintf(stderr,
1846d7802caeSToomas Soome 		    gettext("Error reading bootblock from %s\n"),
1847d7802caeSToomas Soome 		    file);
1848d7802caeSToomas Soome 	}
1849d7802caeSToomas Soome 
1850d7802caeSToomas Soome 	if (retval == BC_NOEXTRA) {
1851d7802caeSToomas Soome 		BOOT_DEBUG("No multiboot header found on %s, unable to "
1852d7802caeSToomas Soome 		    "locate extra information area (old/non versioned "
1853d7802caeSToomas Soome 		    "bootblock?) \n", file);
1854d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("No extended information"
1855d7802caeSToomas Soome 		    " found\n"));
1856d7802caeSToomas Soome 	}
1857d7802caeSToomas Soome 	return (retval);
1858d7802caeSToomas Soome }
1859d7802caeSToomas Soome 
1860d7802caeSToomas Soome static void
add_bootblock_einfo(ib_bootblock_t * bblock,char * updt_str)1861d7802caeSToomas Soome add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
1862d7802caeSToomas Soome {
1863d7802caeSToomas Soome 	bblk_hs_t	hs;
1864d7802caeSToomas Soome 	uint32_t	avail_space;
1865d7802caeSToomas Soome 
1866d7802caeSToomas Soome 	assert(bblock != NULL);
1867d7802caeSToomas Soome 
1868d7802caeSToomas Soome 	if (updt_str == NULL) {
1869d7802caeSToomas Soome 		BOOT_DEBUG("WARNING: no update string passed to "
1870d7802caeSToomas Soome 		    "add_bootblock_einfo()\n");
1871d7802caeSToomas Soome 		return;
1872d7802caeSToomas Soome 	}
1873d7802caeSToomas Soome 
1874d7802caeSToomas Soome 	/* Fill bootblock hashing source information. */
1875d7802caeSToomas Soome 	hs.src_buf = (unsigned char *)bblock->file;
1876d7802caeSToomas Soome 	hs.src_size = bblock->file_size;
1877d7802caeSToomas Soome 	/* How much space for the extended information structure? */
1878d7802caeSToomas Soome 	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
1879d7802caeSToomas Soome 	/* Place the extended information structure. */
1880d7802caeSToomas Soome 	add_einfo(bblock->extra, updt_str, &hs, avail_space);
1881d7802caeSToomas Soome }
1882d7802caeSToomas Soome 
1883d7802caeSToomas Soome /*
1884d7802caeSToomas Soome  * set up data for case stage1 is installed as MBR
1885d7802caeSToomas Soome  * set up location and size of bootblock
1886d7802caeSToomas Soome  * set disk guid to provide unique information for biosdev command
1887d7802caeSToomas Soome  */
1888d7802caeSToomas Soome static void
prepare_stage1(struct partlist * stage1,struct partlist * stage2,uuid_t uuid)1889d7802caeSToomas Soome prepare_stage1(struct partlist *stage1, struct partlist *stage2, uuid_t uuid)
1890d7802caeSToomas Soome {
1891d7802caeSToomas Soome 	char *src, *dest;
1892d7802caeSToomas Soome 	ib_bootblock_t *bblk;
1893d7802caeSToomas Soome 	ib_device_t *device;
1894d7802caeSToomas Soome 	uint16_t size;
1895d7802caeSToomas Soome 	struct mboot *mbr;
1896d7802caeSToomas Soome 
1897d7802caeSToomas Soome 	src = stage1->pl_stage;
1898d7802caeSToomas Soome 	dest = stage1->pl_src_data;
1899d7802caeSToomas Soome 	device = stage2->pl_device;
1900d7802caeSToomas Soome 
1901d7802caeSToomas Soome 	/* Only copy from valid source. */
1902d7802caeSToomas Soome 	mbr = stage1->pl_stage;
1903d7802caeSToomas Soome 	if (mbr->signature == MBB_MAGIC) {
1904d7802caeSToomas Soome 		/* copy BPB */
1905d7802caeSToomas Soome 		bcopy(src + STAGE1_BPB_OFFSET, dest + STAGE1_BPB_OFFSET,
1906d7802caeSToomas Soome 		    STAGE1_BPB_SIZE);
1907d7802caeSToomas Soome 
1908d7802caeSToomas Soome 		/* copy MBR, note STAGE1_SIG == BOOTSZ */
1909d7802caeSToomas Soome 		bcopy(src + STAGE1_SIG, dest + STAGE1_SIG,
1910d7802caeSToomas Soome 		    SECTOR_SIZE - STAGE1_SIG);
1911d7802caeSToomas Soome 	}
1912d7802caeSToomas Soome 
1913d7802caeSToomas Soome 	bcopy(uuid, dest + STAGE1_STAGE2_UUID, UUID_LEN);
1914d7802caeSToomas Soome 
1915d7802caeSToomas Soome 	/* set stage2 size */
1916d7802caeSToomas Soome 	bblk = stage2->pl_src_data;
1917d7802caeSToomas Soome 	size = bblk->buf_size / SECTOR_SIZE;
1918d7802caeSToomas Soome 	*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = size;
1919d7802caeSToomas Soome 
1920d7802caeSToomas Soome 	/* set stage2 LBA */
1921d7802caeSToomas Soome 	*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
1922d7802caeSToomas Soome 	    device->stage.start + device->stage.offset;
1923d7802caeSToomas Soome 
1924d7802caeSToomas Soome 	/* Copy prepared data to stage1 block read from the disk. */
1925d7802caeSToomas Soome 	bcopy(dest, src, SECTOR_SIZE);
1926d7802caeSToomas Soome }
1927d7802caeSToomas Soome 
1928d7802caeSToomas Soome static void
prepare_bootblock(ib_data_t * data,struct partlist * pl,char * updt_str)1929d7802caeSToomas Soome prepare_bootblock(ib_data_t *data, struct partlist *pl, char *updt_str)
1930d7802caeSToomas Soome {
1931d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
1932d7802caeSToomas Soome 	uint64_t		*ptr;
1933d7802caeSToomas Soome 
1934d7802caeSToomas Soome 	assert(pl != NULL);
1935d7802caeSToomas Soome 
1936d7802caeSToomas Soome 	bblock = pl->pl_src_data;
193746d70dceSToomas Soome 	if (bblock == NULL)
193846d70dceSToomas Soome 		return;
1939d7802caeSToomas Soome 
1940d7802caeSToomas Soome 	ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
1941d7802caeSToomas Soome 	*ptr = data->target.start;
1942d7802caeSToomas Soome 
1943d7802caeSToomas Soome 	/*
1944d7802caeSToomas Soome 	 * the loader bootblock has built in version, if custom
1945d7802caeSToomas Soome 	 * version was provided, update it.
1946d7802caeSToomas Soome 	 */
1947d7802caeSToomas Soome 	if (do_version)
1948d7802caeSToomas Soome 		add_bootblock_einfo(bblock, updt_str);
19490c946d80SToomas Soome }
19500c946d80SToomas Soome 
19510c946d80SToomas Soome static int
open_device(const char * path)1952d7802caeSToomas Soome open_device(const char *path)
19530c946d80SToomas Soome {
19540c946d80SToomas Soome 	struct stat	statbuf = {0};
19550c946d80SToomas Soome 	int		fd = -1;
19560c946d80SToomas Soome 
19570c946d80SToomas Soome 	if (nowrite)
19580c946d80SToomas Soome 		fd = open(path, O_RDONLY);
19590c946d80SToomas Soome 	else
19600c946d80SToomas Soome 		fd = open(path, O_RDWR);
19610c946d80SToomas Soome 
19620c946d80SToomas Soome 	if (fd == -1) {
19630c946d80SToomas Soome 		BOOT_DEBUG("Unable to open %s\n", path);
19640c946d80SToomas Soome 		perror("open");
19650c946d80SToomas Soome 		return (-1);
19660c946d80SToomas Soome 	}
19670c946d80SToomas Soome 
19680c946d80SToomas Soome 	if (fstat(fd, &statbuf) != 0) {
19690c946d80SToomas Soome 		BOOT_DEBUG("Unable to stat %s\n", path);
19700c946d80SToomas Soome 		perror("stat");
19710c946d80SToomas Soome 		(void) close(fd);
19720c946d80SToomas Soome 		return (-1);
19730c946d80SToomas Soome 	}
19740c946d80SToomas Soome 
19750c946d80SToomas Soome 	if (S_ISCHR(statbuf.st_mode) == 0) {
19760c946d80SToomas Soome 		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
19770c946d80SToomas Soome 		    path);
19780c946d80SToomas Soome 		(void) close(fd);
19790c946d80SToomas Soome 		return (-1);
19800c946d80SToomas Soome 	}
19810c946d80SToomas Soome 
19820c946d80SToomas Soome 	return (fd);
19830c946d80SToomas Soome }
19840c946d80SToomas Soome 
1985d7802caeSToomas Soome /*
1986d7802caeSToomas Soome  * We need to record stage2 location and size into pmbr/vbr.
1987d7802caeSToomas Soome  * We need to record target partiton LBA to stage2.
1988d7802caeSToomas Soome  */
1989d7802caeSToomas Soome static void
prepare_bblocks(ib_data_t * data)1990d7802caeSToomas Soome prepare_bblocks(ib_data_t *data)
19910c946d80SToomas Soome {
1992d7802caeSToomas Soome 	struct partlist *pl;
1993d7802caeSToomas Soome 	struct partlist *mbr, *stage1, *stage2;
1994d7802caeSToomas Soome 	uuid_t uuid;
19950c946d80SToomas Soome 
1996*6b02bf31SToomas Soome 	/*
1997*6b02bf31SToomas Soome 	 * Create disk uuid. We only need reasonable amount of uniqueness
1998*6b02bf31SToomas Soome 	 * to allow biosdev to identify disk based on mbr differences.
1999*6b02bf31SToomas Soome 	 */
2000*6b02bf31SToomas Soome 	uuid_generate(uuid);
2001*6b02bf31SToomas Soome 
2002d7802caeSToomas Soome 	mbr = stage1 = stage2 = NULL;
2003*6b02bf31SToomas Soome 
2004*6b02bf31SToomas Soome 	/* First find stage 2. */
2005*6b02bf31SToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
2006*6b02bf31SToomas Soome 		if (pl->pl_type == IB_BBLK_STAGE2) {
2007*6b02bf31SToomas Soome 			stage2 = pl;
2008*6b02bf31SToomas Soome 
2009*6b02bf31SToomas Soome 			/*
2010*6b02bf31SToomas Soome 			 * When stage2 needs update, make sure we also
2011*6b02bf31SToomas Soome 			 * update stage1.
2012*6b02bf31SToomas Soome 			 */
2013*6b02bf31SToomas Soome 			if (pl->pl_cb.compare != NULL &&
2014*6b02bf31SToomas Soome 			    pl->pl_cb.compare(pl))
2015*6b02bf31SToomas Soome 				write_vbr = true;
2016*6b02bf31SToomas Soome 			break;
2017*6b02bf31SToomas Soome 		}
2018*6b02bf31SToomas Soome 	}
2019d7802caeSToomas Soome 	/*
2020d7802caeSToomas Soome 	 * Walk list and pick up BIOS boot blocks. EFI boot programs
2021d7802caeSToomas Soome 	 * can be set in place.
2022d7802caeSToomas Soome 	 */
2023d7802caeSToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
2024d7802caeSToomas Soome 		switch (pl->pl_type) {
2025d7802caeSToomas Soome 		case IB_BBLK_MBR:
2026d7802caeSToomas Soome 			mbr = pl;
2027d7802caeSToomas Soome 			break;
2028d7802caeSToomas Soome 		case IB_BBLK_STAGE1:
2029d7802caeSToomas Soome 			stage1 = pl;
2030*6b02bf31SToomas Soome 			if (stage2 != NULL)
2031*6b02bf31SToomas Soome 				prepare_stage1(stage1, stage2, uuid);
2032d7802caeSToomas Soome 			break;
2033d7802caeSToomas Soome 		case IB_BBLK_STAGE2:
2034d7802caeSToomas Soome 		case IB_BBLK_EFI:
2035d7802caeSToomas Soome 			prepare_bootblock(data, pl, update_str);
2036d7802caeSToomas Soome 			break;
2037d7802caeSToomas Soome 		default:
20380c946d80SToomas Soome 			break;
20390c946d80SToomas Soome 		}
20400c946d80SToomas Soome 	}
20410c946d80SToomas Soome 
2042d7802caeSToomas Soome 	/* If stage2 is missing, we are done. */
2043d7802caeSToomas Soome 	if (stage2 == NULL)
2044d7802caeSToomas Soome 		return;
20450c946d80SToomas Soome 
2046d7802caeSToomas Soome 	if (mbr != NULL) {
2047d7802caeSToomas Soome 		prepare_stage1(mbr, stage2, uuid);
20480c946d80SToomas Soome 
20490c946d80SToomas Soome 		/*
2050d7802caeSToomas Soome 		 * If we have stage1, we point MBR to read stage 1.
20510c946d80SToomas Soome 		 */
2052d7802caeSToomas Soome 		if (stage1 != NULL) {
2053d7802caeSToomas Soome 			char *dest = mbr->pl_stage;
20540c946d80SToomas Soome 
2055d7802caeSToomas Soome 			*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = 1;
2056d7802caeSToomas Soome 			*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
2057d7802caeSToomas Soome 			    stage1->pl_device->stage.start;
20580c946d80SToomas Soome 		}
20590c946d80SToomas Soome 	}
20600c946d80SToomas Soome }
20610c946d80SToomas Soome 
20620c946d80SToomas Soome /*
20630c946d80SToomas Soome  * Install a new bootblock on the given device. handle_install() expects argv
20640c946d80SToomas Soome  * to contain 3 parameters (the target device path and the path to the
20650c946d80SToomas Soome  * bootblock.
20660c946d80SToomas Soome  *
20670c946d80SToomas Soome  * Returns:	BC_SUCCESS - if the installation is successful
20680c946d80SToomas Soome  *		BC_ERROR   - if the installation failed
20690c946d80SToomas Soome  *		BC_NOUPDT  - if no installation was performed because the
20700c946d80SToomas Soome  *		             version currently installed is more recent than the
20710c946d80SToomas Soome  *			     supplied one.
20720c946d80SToomas Soome  *
20730c946d80SToomas Soome  */
20740c946d80SToomas Soome static int
handle_install(char * progname,int argc,char ** argv)2075d7802caeSToomas Soome handle_install(char *progname, int argc, char **argv)
20760c946d80SToomas Soome {
2077d7802caeSToomas Soome 	struct partlist	*pl;
2078d7802caeSToomas Soome 	ib_data_t	data = { 0 };
20790c946d80SToomas Soome 	char		*device_path = NULL;
20800c946d80SToomas Soome 	int		ret = BC_ERROR;
20810c946d80SToomas Soome 
2082d7802caeSToomas Soome 	switch (argc) {
2083d7802caeSToomas Soome 	case 1:
2084d7802caeSToomas Soome 		if ((device_path = strdup(argv[0])) == NULL) {
2085d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2086d7802caeSToomas Soome 			goto done;
2087d7802caeSToomas Soome 		}
2088d7802caeSToomas Soome 		if (asprintf(&stage1, "%s/%s", boot_dir, STAGE1) < 0) {
2089d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2090d7802caeSToomas Soome 			goto done;
2091d7802caeSToomas Soome 		}
2092d7802caeSToomas Soome 		if (asprintf(&stage2, "%s/%s", boot_dir, STAGE2) < 0) {
2093d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2094d7802caeSToomas Soome 			goto done;
2095d7802caeSToomas Soome 		}
2096d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2097d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2098d7802caeSToomas Soome 			goto done;
2099d7802caeSToomas Soome 		}
2100d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2101d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2102d7802caeSToomas Soome 			goto done;
2103d7802caeSToomas Soome 		}
2104d7802caeSToomas Soome 		break;
2105d7802caeSToomas Soome 	case 3:
2106d7802caeSToomas Soome 		if ((stage1 = strdup(argv[0])) == NULL) {
2107d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2108d7802caeSToomas Soome 			goto done;
2109d7802caeSToomas Soome 		}
2110d7802caeSToomas Soome 		if ((stage2 = strdup(argv[1])) == NULL) {
2111d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2112d7802caeSToomas Soome 			goto done;
2113d7802caeSToomas Soome 		}
2114d7802caeSToomas Soome 		if ((device_path = strdup(argv[2])) == NULL) {
2115d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2116d7802caeSToomas Soome 			goto done;
2117d7802caeSToomas Soome 		}
2118d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2119d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2120d7802caeSToomas Soome 			goto done;
2121d7802caeSToomas Soome 		}
2122d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2123d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2124d7802caeSToomas Soome 			goto done;
2125d7802caeSToomas Soome 		}
2126d7802caeSToomas Soome 		break;
2127d7802caeSToomas Soome 	default:
2128d7802caeSToomas Soome 		usage(progname, ret);
2129d7802caeSToomas Soome 	}
21300c946d80SToomas Soome 
2131d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2132d7802caeSToomas Soome 	if (data.plist == NULL) {
2133d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2134d7802caeSToomas Soome 		goto done;
21350c946d80SToomas Soome 	}
2136d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
21370c946d80SToomas Soome 
21380c946d80SToomas Soome 	BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
2139d7802caeSToomas Soome 	    device_path, stage1, stage2);
21400c946d80SToomas Soome 
2141d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2142d7802caeSToomas Soome 		/* Read all data. */
2143d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2144d7802caeSToomas Soome 			if (!pl->pl_cb.read(pl)) {
2145d7802caeSToomas Soome 				printf("\n");
2146d7802caeSToomas Soome 			}
2147d7802caeSToomas Soome 			if (!pl->pl_cb.read_bbl(pl)) {
214846d70dceSToomas Soome 				/*
214946d70dceSToomas Soome 				 * We will ignore ESP updates in case of
215046d70dceSToomas Soome 				 * older system where we are missing
215146d70dceSToomas Soome 				 * loader64.efi and loader32.efi.
215246d70dceSToomas Soome 				 */
215346d70dceSToomas Soome 				if (pl->pl_type != IB_BBLK_EFI)
215446d70dceSToomas Soome 					goto cleanup;
2155d7802caeSToomas Soome 			}
2156d7802caeSToomas Soome 		}
21570c946d80SToomas Soome 
2158d7802caeSToomas Soome 		/* Prepare data. */
2159d7802caeSToomas Soome 		prepare_bblocks(&data);
2160d7802caeSToomas Soome 
2161d7802caeSToomas Soome 		/* Commit data to disk. */
2162d7802caeSToomas Soome 		while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) !=
2163d7802caeSToomas Soome 		    NULL) {
2164d7802caeSToomas Soome 			if (pl->pl_cb.compare != NULL &&
2165d7802caeSToomas Soome 			    pl->pl_cb.compare(pl)) {
2166d7802caeSToomas Soome 				if (pl->pl_cb.install != NULL)
2167d7802caeSToomas Soome 					pl->pl_cb.install(&data, pl);
2168d7802caeSToomas Soome 			}
2169d7802caeSToomas Soome 			STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2170d7802caeSToomas Soome 			partlist_free(pl);
2171d7802caeSToomas Soome 		}
21720c946d80SToomas Soome 	}
2173d7802caeSToomas Soome 	ret = BC_SUCCESS;
21740c946d80SToomas Soome 
2175d7802caeSToomas Soome cleanup:
2176d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2177d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2178d7802caeSToomas Soome 		partlist_free(pl);
21790c946d80SToomas Soome 	}
2180d7802caeSToomas Soome 	free(data.plist);
2181d7802caeSToomas Soome done:
21820c946d80SToomas Soome 	free(stage1);
2183d7802caeSToomas Soome 	free(stage2);
2184d7802caeSToomas Soome 	free(efi32);
2185d7802caeSToomas Soome 	free(efi64);
21860c946d80SToomas Soome 	free(device_path);
21870c946d80SToomas Soome 	return (ret);
21880c946d80SToomas Soome }
21890c946d80SToomas Soome 
21900c946d80SToomas Soome /*
21910c946d80SToomas Soome  * Retrieves from a device the extended information (einfo) associated to the
2192b6dfa2aeSToomas Soome  * file or installed stage2.
2193b6dfa2aeSToomas Soome  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
2194b6dfa2aeSToomas Soome  * or file name.
21950c946d80SToomas Soome  * Returns:
21960c946d80SToomas Soome  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
21970c946d80SToomas Soome  *	  - BC_ERROR (on error)
21980c946d80SToomas Soome  *        - BC_NOEINFO (no extended information available)
21990c946d80SToomas Soome  */
22000c946d80SToomas Soome static int
handle_getinfo(char * progname,int argc,char ** argv)2201d7802caeSToomas Soome handle_getinfo(char *progname, int argc, char **argv)
22020c946d80SToomas Soome {
2203d7802caeSToomas Soome 	struct partlist	*pl;
2204d7802caeSToomas Soome 	ib_data_t	data = { 0 };
2205d7802caeSToomas Soome 	char		*device_path;
22060c946d80SToomas Soome 
2207d7802caeSToomas Soome 	if (argc != 1) {
22080c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2209bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22100c946d80SToomas Soome 	}
22110c946d80SToomas Soome 
2212d7802caeSToomas Soome 	if ((device_path = strdup(argv[0])) == NULL) {
2213d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2214d7802caeSToomas Soome 		return (BC_ERROR);
22150c946d80SToomas Soome 	}
22160c946d80SToomas Soome 
2217d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2218d7802caeSToomas Soome 	if (data.plist == NULL) {
2219d7802caeSToomas Soome 		perror("malloc");
2220d7802caeSToomas Soome 		free(device_path);
2221d7802caeSToomas Soome 		return (BC_ERROR);
22220c946d80SToomas Soome 	}
2223d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
22240c946d80SToomas Soome 
2225d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2226d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2227d7802caeSToomas Soome 			if (pl->pl_cb.read(pl))
2228d7802caeSToomas Soome 				pl->pl_cb.print(pl);
2229d7802caeSToomas Soome 			else
2230d7802caeSToomas Soome 				printf("\n");
2231d7802caeSToomas Soome 		}
22320c946d80SToomas Soome 	}
22330c946d80SToomas Soome 
2234d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2235d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2236d7802caeSToomas Soome 		partlist_free(pl);
22370c946d80SToomas Soome 	}
2238d7802caeSToomas Soome 	free(data.plist);
22390c946d80SToomas Soome 
2240d7802caeSToomas Soome 	return (BC_SUCCESS);
22410c946d80SToomas Soome }
22420c946d80SToomas Soome 
22430c946d80SToomas Soome /*
22440c946d80SToomas Soome  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
22450c946d80SToomas Soome  *
22460c946d80SToomas Soome  * Returns:
22470c946d80SToomas Soome  *	- BC_SUCCESS (a successful propagation happened)
22480c946d80SToomas Soome  *	- BC_ERROR (an error occurred)
22490c946d80SToomas Soome  *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
22500c946d80SToomas Soome  *			there is no multiboot information)
22510c946d80SToomas Soome  */
22520c946d80SToomas Soome static int
handle_mirror(char * progname,int argc,char ** argv)2253d7802caeSToomas Soome handle_mirror(char *progname, int argc, char **argv)
22540c946d80SToomas Soome {
2255d7802caeSToomas Soome 	ib_data_t src = { 0 };
2256d7802caeSToomas Soome 	ib_data_t dest = { 0 };
2257d7802caeSToomas Soome 	struct partlist *pl_src, *pl_dest;
2258d7802caeSToomas Soome 	char		*curr_device_path = NULL;
2259d7802caeSToomas Soome 	char		*attach_device_path = NULL;
22600c946d80SToomas Soome 	int		retval = BC_ERROR;
22610c946d80SToomas Soome 
2262d7802caeSToomas Soome 	if (argc == 2) {
2263d7802caeSToomas Soome 		curr_device_path = strdup(argv[0]);
2264d7802caeSToomas Soome 		attach_device_path = strdup(argv[1]);
2265d7802caeSToomas Soome 	}
22660c946d80SToomas Soome 
22670c946d80SToomas Soome 	if (!curr_device_path || !attach_device_path) {
2268d7802caeSToomas Soome 		free(curr_device_path);
2269d7802caeSToomas Soome 		free(attach_device_path);
22700c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2271bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22720c946d80SToomas Soome 	}
22730c946d80SToomas Soome 	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
22740c946d80SToomas Soome 	    " %s\n", curr_device_path, attach_device_path);
22750c946d80SToomas Soome 
2276d7802caeSToomas Soome 	src.plist = malloc(sizeof (*src.plist));
2277d7802caeSToomas Soome 	if (src.plist == NULL) {
2278d7802caeSToomas Soome 		perror("malloc");
2279d7802caeSToomas Soome 		return (BC_ERROR);
2280d7802caeSToomas Soome 	}
2281d7802caeSToomas Soome 	STAILQ_INIT(src.plist);
2282d7802caeSToomas Soome 
2283d7802caeSToomas Soome 	dest.plist = malloc(sizeof (*dest.plist));
2284d7802caeSToomas Soome 	if (dest.plist == NULL) {
2285d7802caeSToomas Soome 		perror("malloc");
2286d7802caeSToomas Soome 		goto out;
2287d7802caeSToomas Soome 	}
2288d7802caeSToomas Soome 	STAILQ_INIT(dest.plist);
22890c946d80SToomas Soome 
2290d7802caeSToomas Soome 	if (!probe_device(&src, curr_device_path)) {
22910c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22920c946d80SToomas Soome 		    "information from %s (current device)\n"),
22930c946d80SToomas Soome 		    curr_device_path);
2294d7802caeSToomas Soome 		goto out;
22950c946d80SToomas Soome 	}
22960c946d80SToomas Soome 
2297d7802caeSToomas Soome 	if (!probe_device(&dest, attach_device_path) != BC_SUCCESS) {
22980c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
22990c946d80SToomas Soome 		    "information from %s (attaching device)\n"),
23000c946d80SToomas Soome 		    attach_device_path);
2301d7802caeSToomas Soome 		goto cleanup_src;
23020c946d80SToomas Soome 	}
23030c946d80SToomas Soome 
23049937ff19SToomas Soome 	write_vbr = true;
2305d7802caeSToomas Soome 	write_mbr = true;
2306d7802caeSToomas Soome 	force_mbr = true;
2307d7802caeSToomas Soome 
2308d7802caeSToomas Soome 	pl_dest = STAILQ_FIRST(dest.plist);
2309d7802caeSToomas Soome 	STAILQ_FOREACH(pl_src, src.plist, pl_next) {
2310d7802caeSToomas Soome 		if (pl_dest == NULL) {
2311d7802caeSToomas Soome 			(void) fprintf(stderr,
2312d7802caeSToomas Soome 			    gettext("Destination disk layout is different "
2313d7802caeSToomas Soome 			    "from source, can not mirror.\n"));
2314d7802caeSToomas Soome 			goto cleanup;
2315d7802caeSToomas Soome 		}
2316d7802caeSToomas Soome 		if (!pl_src->pl_cb.read(pl_src)) {
2317d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2318d7802caeSToomas Soome 			    "boot block from %s\n"), pl_src->pl_devname);
2319d7802caeSToomas Soome 			goto cleanup;
2320d7802caeSToomas Soome 		}
2321d7802caeSToomas Soome 		if (!pl_dest->pl_cb.read(pl_dest)) {
2322d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2323d7802caeSToomas Soome 			    "boot block from %s\n"), pl_dest->pl_devname);
2324d7802caeSToomas Soome 		}
2325d7802caeSToomas Soome 
2326d7802caeSToomas Soome 		/* Set source pl_stage to destination source data */
2327d7802caeSToomas Soome 		pl_dest->pl_src_data = pl_src->pl_stage;
2328d7802caeSToomas Soome 		pl_src->pl_stage = NULL;
23290c946d80SToomas Soome 
2330d7802caeSToomas Soome 		pl_dest = STAILQ_NEXT(pl_dest, pl_next);
23310c946d80SToomas Soome 	}
23320c946d80SToomas Soome 
2333d7802caeSToomas Soome 	/* Prepare data. */
2334d7802caeSToomas Soome 	prepare_bblocks(&dest);
23350c946d80SToomas Soome 
2336d7802caeSToomas Soome 	/* Commit data to disk. */
2337d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2338d7802caeSToomas Soome 		pl_dest->pl_cb.install(&dest, pl_dest);
2339d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2340d7802caeSToomas Soome 		partlist_free(pl_dest);
2341d7802caeSToomas Soome 
2342d7802caeSToomas Soome 		/* Free source list */
2343d7802caeSToomas Soome 		pl_src = STAILQ_LAST(src.plist, partlist, pl_next);
2344d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2345d7802caeSToomas Soome 		partlist_free(pl_src);
2346d7802caeSToomas Soome 	}
2347d7802caeSToomas Soome 	retval = BC_SUCCESS;
2348d7802caeSToomas Soome 
2349d7802caeSToomas Soome cleanup:
2350d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2351d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2352d7802caeSToomas Soome 		partlist_free(pl_dest);
2353d7802caeSToomas Soome 	}
2354d7802caeSToomas Soome 	free(dest.plist);
2355d7802caeSToomas Soome cleanup_src:
2356d7802caeSToomas Soome 	while ((pl_src = STAILQ_LAST(src.plist, partlist, pl_next)) != NULL) {
2357d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2358d7802caeSToomas Soome 		partlist_free(pl_src);
2359d7802caeSToomas Soome 	}
2360d7802caeSToomas Soome 	free(src.plist);
2361d7802caeSToomas Soome out:
23620c946d80SToomas Soome 	free(curr_device_path);
23630c946d80SToomas Soome 	free(attach_device_path);
23640c946d80SToomas Soome 	return (retval);
23650c946d80SToomas Soome }
23660c946d80SToomas Soome 
2367d7802caeSToomas Soome #define	USAGE_STRING	\
2368d7802caeSToomas Soome "Usage:\t%s [-fFmn] [-b boot_dir] [-u verstr]\n"	\
2369d7802caeSToomas Soome "\t\t[stage1 stage2] raw-device\n"			\
2370d7802caeSToomas Soome "\t%s -M [-n] raw-device attach-raw-device\n"		\
2371d7802caeSToomas Soome "\t%s [-e|-V] -i raw-device | file\n"
23720c946d80SToomas Soome 
23730c946d80SToomas Soome #define	CANON_USAGE_STR	gettext(USAGE_STRING)
23740c946d80SToomas Soome 
23750c946d80SToomas Soome static void
usage(char * progname,int rc)2376bdecfb1eSToomas Soome usage(char *progname, int rc)
23770c946d80SToomas Soome {
23780c946d80SToomas Soome 	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
2379d7802caeSToomas Soome 	fini_yes();
2380bdecfb1eSToomas Soome 	exit(rc);
23810c946d80SToomas Soome }
23820c946d80SToomas Soome 
23830c946d80SToomas Soome int
main(int argc,char ** argv)23840c946d80SToomas Soome main(int argc, char **argv)
23850c946d80SToomas Soome {
23860c946d80SToomas Soome 	int	opt;
23870c946d80SToomas Soome 	int	ret;
23880c946d80SToomas Soome 	char	*progname;
2389d7802caeSToomas Soome 	struct stat sb;
23900c946d80SToomas Soome 
23910c946d80SToomas Soome 	(void) setlocale(LC_ALL, "");
23920c946d80SToomas Soome 	(void) textdomain(TEXT_DOMAIN);
2393bdecfb1eSToomas Soome 	if (init_yes() < 0)
2394bdecfb1eSToomas Soome 		errx(BC_ERROR, gettext(ERR_MSG_INIT_YES), strerror(errno));
2395bdecfb1eSToomas Soome 
2396d7802caeSToomas Soome 	/* Needed for mount pcfs. */
2397d7802caeSToomas Soome 	tzset();
2398d7802caeSToomas Soome 
2399bdecfb1eSToomas Soome 	/* Determine our name */
2400bdecfb1eSToomas Soome 	progname = basename(argv[0]);
24010c946d80SToomas Soome 
2402d7802caeSToomas Soome 	while ((opt = getopt(argc, argv, "b:deFfhiMmnu:V")) != EOF) {
24030c946d80SToomas Soome 		switch (opt) {
2404d7802caeSToomas Soome 		case 'b':
2405d7802caeSToomas Soome 			boot_dir = strdup(optarg);
2406d7802caeSToomas Soome 			if (boot_dir == NULL) {
2407d7802caeSToomas Soome 				err(BC_ERROR,
2408d7802caeSToomas Soome 				    gettext("Memory allocation failure"));
2409d7802caeSToomas Soome 			}
2410d7802caeSToomas Soome 			if (lstat(boot_dir, &sb) != 0) {
2411d7802caeSToomas Soome 				err(BC_ERROR, boot_dir);
2412d7802caeSToomas Soome 			}
2413d7802caeSToomas Soome 			if (!S_ISDIR(sb.st_mode)) {
2414d7802caeSToomas Soome 				errx(BC_ERROR, gettext("%s: not a directory"),
2415d7802caeSToomas Soome 				    boot_dir);
2416d7802caeSToomas Soome 			}
2417d7802caeSToomas Soome 			break;
24180c946d80SToomas Soome 		case 'd':
2419d7802caeSToomas Soome 			boot_debug = true;
24200c946d80SToomas Soome 			break;
24210c946d80SToomas Soome 		case 'e':
2422d7802caeSToomas Soome 			strip = true;
24230c946d80SToomas Soome 			break;
24240c946d80SToomas Soome 		case 'F':
2425d7802caeSToomas Soome 			force_update = true;
24260c946d80SToomas Soome 			break;
24270c946d80SToomas Soome 		case 'f':
2428d7802caeSToomas Soome 			force_mbr = true;
24290c946d80SToomas Soome 			break;
24300c946d80SToomas Soome 		case 'h':
2431bdecfb1eSToomas Soome 			usage(progname, BC_SUCCESS);
24320c946d80SToomas Soome 			break;
24330c946d80SToomas Soome 		case 'i':
2434d7802caeSToomas Soome 			do_getinfo = true;
24350c946d80SToomas Soome 			break;
24360c946d80SToomas Soome 		case 'M':
2437d7802caeSToomas Soome 			do_mirror_bblk = true;
24380c946d80SToomas Soome 			break;
24390c946d80SToomas Soome 		case 'm':
2440d7802caeSToomas Soome 			write_mbr = true;
24410c946d80SToomas Soome 			break;
24420c946d80SToomas Soome 		case 'n':
2443d7802caeSToomas Soome 			nowrite = true;
24440c946d80SToomas Soome 			break;
24450c946d80SToomas Soome 		case 'u':
2446d7802caeSToomas Soome 			do_version = true;
24470c946d80SToomas Soome 
2448bdecfb1eSToomas Soome 			update_str = strdup(optarg);
24490c946d80SToomas Soome 			if (update_str == NULL) {
24500c946d80SToomas Soome 				perror(gettext("Memory allocation failure"));
24510c946d80SToomas Soome 				exit(BC_ERROR);
24520c946d80SToomas Soome 			}
24530c946d80SToomas Soome 			break;
24540c946d80SToomas Soome 		case 'V':
2455d7802caeSToomas Soome 			verbose_dump = true;
24560c946d80SToomas Soome 			break;
24570c946d80SToomas Soome 		default:
24580c946d80SToomas Soome 			/* fall through to process non-optional args */
24590c946d80SToomas Soome 			break;
24600c946d80SToomas Soome 		}
24610c946d80SToomas Soome 	}
24620c946d80SToomas Soome 
24630c946d80SToomas Soome 	/* check arguments */
24640c946d80SToomas Soome 	check_options(progname);
24650c946d80SToomas Soome 
24660c946d80SToomas Soome 	if (nowrite)
24670c946d80SToomas Soome 		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
24680c946d80SToomas Soome 		    " be written to disk.\n"));
24690c946d80SToomas Soome 
24700c946d80SToomas Soome 	if (do_getinfo) {
2471d7802caeSToomas Soome 		ret = handle_getinfo(progname, argc - optind, argv + optind);
24720c946d80SToomas Soome 	} else if (do_mirror_bblk) {
2473d7802caeSToomas Soome 		ret = handle_mirror(progname, argc - optind, argv + optind);
24740c946d80SToomas Soome 	} else {
2475d7802caeSToomas Soome 		ret = handle_install(progname, argc - optind, argv + optind);
24760c946d80SToomas Soome 	}
2477d7802caeSToomas Soome 	fini_yes();
24780c946d80SToomas Soome 	return (ret);
24790c946d80SToomas Soome }
24800c946d80SToomas Soome 
24810c946d80SToomas Soome #define	MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
24820c946d80SToomas Soome static void
check_options(char * progname)24830c946d80SToomas Soome check_options(char *progname)
24840c946d80SToomas Soome {
24850c946d80SToomas Soome 	if (do_getinfo && do_mirror_bblk) {
24860c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
24870c946d80SToomas Soome 		    "specified at the same time\n"));
2488bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
24890c946d80SToomas Soome 	}
24900c946d80SToomas Soome 
24910c946d80SToomas Soome 	if (do_mirror_bblk) {
24920c946d80SToomas Soome 		/*
24930c946d80SToomas Soome 		 * -u and -F may actually reflect a user intent that is not
24940c946d80SToomas Soome 		 * correct with this command (mirror can be interpreted
24950c946d80SToomas Soome 		 * "similar" to install. Emit a message and continue.
24960c946d80SToomas Soome 		 * -e and -V have no meaning, be quiet here and only report the
24970c946d80SToomas Soome 		 * incongruence if a debug output is requested.
24980c946d80SToomas Soome 		 */
24990c946d80SToomas Soome 		if (do_version) {
25000c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-u");
2501d7802caeSToomas Soome 			do_version = false;
25020c946d80SToomas Soome 		}
25030c946d80SToomas Soome 		if (force_update) {
25040c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-F");
2505d7802caeSToomas Soome 			force_update = false;
25060c946d80SToomas Soome 		}
25070c946d80SToomas Soome 		if (strip || verbose_dump) {
25080c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
2509d7802caeSToomas Soome 			strip = false;
2510d7802caeSToomas Soome 			verbose_dump = false;
25110c946d80SToomas Soome 		}
25120c946d80SToomas Soome 	}
25130c946d80SToomas Soome 
2514d7802caeSToomas Soome 	if ((strip || verbose_dump) && !do_getinfo)
2515d7802caeSToomas Soome 		usage(progname, BC_ERROR);
2516d7802caeSToomas Soome 
25170c946d80SToomas Soome 	if (do_getinfo) {
25180c946d80SToomas Soome 		if (write_mbr || force_mbr || do_version || force_update) {
25190c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
2520d7802caeSToomas Soome 			write_mbr = force_mbr = do_version = false;
2521d7802caeSToomas Soome 			force_update = false;
25220c946d80SToomas Soome 		}
25230c946d80SToomas Soome 	}
25240c946d80SToomas Soome }
2525