xref: /illumos-gate/usr/src/cmd/boot/installboot/i386/installboot.c (revision 0cc97882c52f30e61739644e480f5f9f980b1621)
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)
174d7802caeSToomas Soome {
175d7802caeSToomas Soome 	char *path;
176d7802caeSToomas Soome 	char optbuf[MAX_MNTOPT_STR] = { '\0', };
177d7802caeSToomas Soome 	char *template = strdup("/tmp/ibootXXXXXX");
178d7802caeSToomas Soome 	int ret;
179d7802caeSToomas Soome 
180d7802caeSToomas Soome 	if (template == NULL)
181d7802caeSToomas Soome 		return (NULL);
182d7802caeSToomas Soome 
183d7802caeSToomas Soome 	if ((path = mkdtemp(template)) == NULL) {
184d7802caeSToomas Soome 		free(template);
185d7802caeSToomas Soome 		return (NULL);
186d7802caeSToomas 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);
199d7802caeSToomas Soome }
200d7802caeSToomas Soome 
201d7802caeSToomas Soome static void
install_stage1_cb(void * data,struct partlist * plist)202d7802caeSToomas Soome install_stage1_cb(void *data, struct partlist *plist)
203d7802caeSToomas Soome {
204d7802caeSToomas Soome 	int rv, fd;
205d7802caeSToomas Soome 	ib_device_t *device = plist->pl_device;
206d7802caeSToomas Soome 
207d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR && !write_mbr)
208d7802caeSToomas Soome 		return;
209d7802caeSToomas 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);
213d7802caeSToomas Soome 		perror("open");
214d7802caeSToomas Soome 		return;
215d7802caeSToomas Soome 	}
216d7802caeSToomas 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);
227d7802caeSToomas Soome 	}
228d7802caeSToomas Soome }
229d7802caeSToomas 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;
237d7802caeSToomas 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 	}
246d7802caeSToomas 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 	}
255*0cc97882SToomas 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,
265*0cc97882SToomas Soome 	    bblock->buf_size / sector_size, 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) {
381d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
382d7802caeSToomas Soome 		return;
383d7802caeSToomas Soome 	}
384d7802caeSToomas 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;
401d7802caeSToomas Soome 	}
402d7802caeSToomas 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) {
4566b02bf31SToomas Soome 		(void) printf("%s is newer than one in %s\n",
4576b02bf31SToomas 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 		 */
501d7802caeSToomas 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 		}
509d7802caeSToomas Soome 	}
510d7802caeSToomas 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 	}
520d7802caeSToomas 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 	}
528d7802caeSToomas Soome 
529d7802caeSToomas Soome 	BOOT_DEBUG("Ready to check installed version vs %s\n", update_str);
530d7802caeSToomas 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)
5510c946d80SToomas Soome {
5520c946d80SToomas Soome 	int fd;
553d7802caeSToomas Soome 	bool rv = false;
5540c946d80SToomas 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 	}
565d7802caeSToomas 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:
572d7802caeSToomas Soome 	(void) close(fd);
573d7802caeSToomas Soome 	return (rv);
574d7802caeSToomas Soome }
575d7802caeSToomas 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 
583*0cc97882SToomas Soome 	/* Allocate actual sector size. */
584*0cc97882SToomas Soome 	data = calloc(1, sector_size);
585d7802caeSToomas Soome 	if (data == NULL)
586d7802caeSToomas Soome 		return (rv);
5870c946d80SToomas Soome 
5880c946d80SToomas Soome 	/* read the stage1 file from filesystem */
589d7802caeSToomas Soome 	fd = open(plist->pl_src_name, O_RDONLY);
5900c946d80SToomas Soome 	if (fd == -1 ||
591d7802caeSToomas Soome 	    read(fd, data, SECTOR_SIZE) != SECTOR_SIZE) {
5920c946d80SToomas Soome 		(void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
593d7802caeSToomas Soome 		    plist->pl_src_name);
594d7802caeSToomas Soome 		free(data);
595d7802caeSToomas Soome 		if (fd != -1)
596d7802caeSToomas Soome 			(void) close(fd);
597d7802caeSToomas Soome 		return (rv);
598d7802caeSToomas Soome 	}
599d7802caeSToomas Soome 
600d7802caeSToomas Soome 	plist->pl_src_data = data;
601d7802caeSToomas Soome 	(void) close(fd);
602d7802caeSToomas Soome 	return (true);
603d7802caeSToomas Soome }
604d7802caeSToomas Soome 
605d7802caeSToomas Soome static bool
read_stage2_cb(struct partlist * plist)606d7802caeSToomas Soome read_stage2_cb(struct partlist *plist)
607d7802caeSToomas Soome {
608d7802caeSToomas Soome 	ib_device_t		*device;
609d7802caeSToomas Soome 	ib_bootblock_t		*bblock;
610d7802caeSToomas Soome 	int			fd;
611d7802caeSToomas Soome 	uint32_t		size, offset;
612d7802caeSToomas Soome 	uint32_t		buf_size;
613d7802caeSToomas Soome 	uint32_t		mboot_off;
614d7802caeSToomas Soome 	multiboot_header_t	*mboot;
615b97b1727SToomas Soome 	size_t			scan_size;
616d7802caeSToomas Soome 
617d7802caeSToomas Soome 	bblock = calloc(1, sizeof (ib_bootblock_t));
618d7802caeSToomas Soome 	if (bblock == NULL)
619d7802caeSToomas Soome 		return (false);
620d7802caeSToomas Soome 
621d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1) {
622d7802caeSToomas Soome 		free(bblock);
623d7802caeSToomas Soome 		return (false);
624d7802caeSToomas Soome 	}
625d7802caeSToomas Soome 
626d7802caeSToomas Soome 	device = plist->pl_device;
627d7802caeSToomas Soome 	plist->pl_stage = bblock;
628*0cc97882SToomas Soome 	offset = device->stage.offset * sector_size;
629b97b1727SToomas Soome 	scan_size = MIN(sizeof (mboot_scan),
630b97b1727SToomas Soome 	    (device->stage.size - device->stage.offset) * sector_size);
631d7802caeSToomas Soome 
632b97b1727SToomas Soome 	if (read_in(fd, mboot_scan, scan_size, offset)
633d7802caeSToomas Soome 	    != BC_SUCCESS) {
634d7802caeSToomas Soome 		BOOT_DEBUG("Error reading bootblock area\n");
635d7802caeSToomas Soome 		perror("read");
636d7802caeSToomas Soome 		(void) close(fd);
637d7802caeSToomas Soome 		return (false);
638d7802caeSToomas Soome 	}
639d7802caeSToomas Soome 
640d7802caeSToomas Soome 	/* No multiboot means no chance of knowing bootblock size */
641b97b1727SToomas Soome 	if (find_multiboot(mboot_scan, scan_size, &mboot_off)
642d7802caeSToomas Soome 	    != BC_SUCCESS) {
643d7802caeSToomas Soome 		BOOT_DEBUG("Unable to find multiboot header\n");
644d7802caeSToomas Soome 		(void) close(fd);
645d7802caeSToomas Soome 		return (false);
646d7802caeSToomas Soome 	}
647d7802caeSToomas Soome 	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
648d7802caeSToomas Soome 
649d7802caeSToomas Soome 	/*
650d7802caeSToomas Soome 	 * make sure mboot has sane values
651d7802caeSToomas Soome 	 */
652d7802caeSToomas Soome 	if (mboot->load_end_addr == 0 ||
653d7802caeSToomas Soome 	    mboot->load_end_addr < mboot->load_addr) {
654d7802caeSToomas Soome 		(void) close(fd);
655d7802caeSToomas Soome 		return (false);
656d7802caeSToomas Soome 	}
657d7802caeSToomas Soome 
658d7802caeSToomas Soome 	/*
659d7802caeSToomas Soome 	 * Currently, the amount of space reserved for extra information
660d7802caeSToomas Soome 	 * is "fixed". We may have to scan for the terminating extra payload
661d7802caeSToomas Soome 	 * in the future.
662d7802caeSToomas Soome 	 */
663d7802caeSToomas Soome 	size = mboot->load_end_addr - mboot->load_addr;
664*0cc97882SToomas Soome 	buf_size = P2ROUNDUP(size + sector_size, sector_size);
665d7802caeSToomas Soome 	bblock->file_size = size;
666d7802caeSToomas Soome 
667d7802caeSToomas Soome 	bblock->buf = malloc(buf_size);
668d7802caeSToomas Soome 	if (bblock->buf == NULL) {
669d7802caeSToomas Soome 		BOOT_DEBUG("Unable to allocate enough memory to read"
670d7802caeSToomas Soome 		    " the extra bootblock from the disk\n");
671d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
672d7802caeSToomas Soome 		(void) close(fd);
673d7802caeSToomas Soome 		return (false);
674d7802caeSToomas Soome 	}
675d7802caeSToomas Soome 	bblock->buf_size = buf_size;
676d7802caeSToomas Soome 
677d7802caeSToomas Soome 	if (read_in(fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
678d7802caeSToomas Soome 		BOOT_DEBUG("Error reading the bootblock\n");
679d7802caeSToomas Soome 		(void) free(bblock->buf);
680d7802caeSToomas Soome 		bblock->buf = NULL;
681d7802caeSToomas Soome 		(void) close(fd);
682d7802caeSToomas Soome 		return (false);
683d7802caeSToomas Soome 	}
684d7802caeSToomas Soome 
685d7802caeSToomas Soome 	/* Update pointers. */
686d7802caeSToomas Soome 	bblock->file = bblock->buf;
687d7802caeSToomas Soome 	bblock->mboot_off = mboot_off;
688d7802caeSToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
689d7802caeSToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
690d7802caeSToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
691d7802caeSToomas Soome 
692d7802caeSToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
693d7802caeSToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
694d7802caeSToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
695d7802caeSToomas Soome 
696d7802caeSToomas Soome 	return (true);
697d7802caeSToomas Soome }
698d7802caeSToomas Soome 
699d7802caeSToomas Soome static bool
read_einfo_file_cb(struct partlist * plist)700d7802caeSToomas Soome read_einfo_file_cb(struct partlist *plist)
701d7802caeSToomas Soome {
70246d70dceSToomas Soome 	int rc;
70346d70dceSToomas Soome 	void *stage;
70446d70dceSToomas Soome 
70546d70dceSToomas Soome 	stage = calloc(1, sizeof (ib_bootblock_t));
70646d70dceSToomas Soome 	if (stage == NULL)
707d7802caeSToomas Soome 		return (false);
708d7802caeSToomas Soome 
70946d70dceSToomas Soome 	rc =  read_bootblock_from_file(plist->pl_devname, stage);
71046d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
71146d70dceSToomas Soome 		free(stage);
71246d70dceSToomas Soome 		stage = NULL;
71346d70dceSToomas Soome 	}
71446d70dceSToomas Soome 	plist->pl_stage = stage;
71546d70dceSToomas Soome 	return (rc == BC_SUCCESS);
716d7802caeSToomas Soome }
717d7802caeSToomas Soome 
718d7802caeSToomas Soome static bool
read_stage2_file_cb(struct partlist * plist)719d7802caeSToomas Soome read_stage2_file_cb(struct partlist *plist)
720d7802caeSToomas Soome {
72146d70dceSToomas Soome 	int rc;
72246d70dceSToomas Soome 	void *data;
72346d70dceSToomas Soome 
72446d70dceSToomas Soome 	data = calloc(1, sizeof (ib_bootblock_t));
72546d70dceSToomas Soome 	if (data == NULL)
726d7802caeSToomas Soome 		return (false);
727d7802caeSToomas Soome 
72846d70dceSToomas Soome 	rc = read_bootblock_from_file(plist->pl_src_name, data);
72946d70dceSToomas Soome 	if (rc != BC_SUCCESS) {
73046d70dceSToomas Soome 		free(data);
73146d70dceSToomas Soome 		data = NULL;
73246d70dceSToomas Soome 	}
73346d70dceSToomas Soome 	plist->pl_src_data = data;
73446d70dceSToomas Soome 	return (rc == BC_SUCCESS);
735d7802caeSToomas Soome }
736d7802caeSToomas Soome 
737d7802caeSToomas Soome /*
738d7802caeSToomas Soome  * convert /dev/rdsk/... to /dev/dsk/...
739d7802caeSToomas Soome  */
740d7802caeSToomas Soome static char *
make_blkdev(const char * path)741d7802caeSToomas Soome make_blkdev(const char *path)
742d7802caeSToomas Soome {
743d7802caeSToomas Soome 	char *tmp;
744d7802caeSToomas Soome 	char *ptr = strdup(path);
745d7802caeSToomas Soome 
746d7802caeSToomas Soome 	if (ptr == NULL)
747d7802caeSToomas Soome 		return (ptr);
748d7802caeSToomas Soome 
749d7802caeSToomas Soome 	tmp = strstr(ptr, "rdsk");
750d7802caeSToomas Soome 	if (tmp == NULL) {
751d7802caeSToomas Soome 		free(ptr);
752d7802caeSToomas Soome 		return (NULL); /* Something is very wrong */
753d7802caeSToomas Soome 	}
754d7802caeSToomas Soome 	/* This is safe because we do shorten the string */
755d7802caeSToomas Soome 	(void) memmove(tmp, tmp + 1, strlen(tmp));
756d7802caeSToomas Soome 	return (ptr);
757d7802caeSToomas Soome }
758d7802caeSToomas Soome 
759d7802caeSToomas Soome /*
760d7802caeSToomas Soome  * Try to mount ESP and read boot program.
761d7802caeSToomas Soome  */
762d7802caeSToomas Soome static bool
read_einfo_esp_cb(struct partlist * plist)763d7802caeSToomas Soome read_einfo_esp_cb(struct partlist *plist)
764d7802caeSToomas Soome {
765d7802caeSToomas Soome 	fstyp_handle_t fhdl;
766d7802caeSToomas Soome 	const char *fident;
767d7802caeSToomas Soome 	char *blkdev, *path, *file;
768d7802caeSToomas Soome 	bool rv = false;
769d7802caeSToomas Soome 	FILE *fp;
770d7802caeSToomas Soome 	struct mnttab mp, mpref = { 0 };
771d7802caeSToomas Soome 	int fd, ret;
772d7802caeSToomas Soome 
773d7802caeSToomas Soome 	if ((fd = open_device(plist->pl_devname)) == -1)
774d7802caeSToomas Soome 		return (rv);
775d7802caeSToomas Soome 
776d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
777d7802caeSToomas Soome 		(void) close(fd);
778d7802caeSToomas Soome 		return (rv);
779d7802caeSToomas Soome 	}
780d7802caeSToomas Soome 
781d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
782d7802caeSToomas Soome 		fstyp_fini(fhdl);
783d7802caeSToomas Soome 		(void) close(fd);
784d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
785d7802caeSToomas Soome 		    "system type\n"));
786d7802caeSToomas Soome 		return (rv);
787d7802caeSToomas Soome 	}
788d7802caeSToomas Soome 
789d7802caeSToomas Soome 	/* We only do expect pcfs. */
790d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_PCFS) != 0) {
791d7802caeSToomas Soome 		(void) fprintf(stderr,
792d7802caeSToomas Soome 		    gettext("File system %s is not supported.\n"), fident);
793d7802caeSToomas Soome 		fstyp_fini(fhdl);
794d7802caeSToomas Soome 		(void) close(fd);
795d7802caeSToomas Soome 		return (rv);
796d7802caeSToomas Soome 	}
797d7802caeSToomas Soome 	fstyp_fini(fhdl);
798d7802caeSToomas Soome 	(void) close(fd);
799d7802caeSToomas Soome 
800d7802caeSToomas Soome 	blkdev = make_blkdev(plist->pl_devname);
801d7802caeSToomas Soome 	if (blkdev == NULL)
802d7802caeSToomas Soome 		return (rv);
803d7802caeSToomas Soome 
804d7802caeSToomas Soome 	/* mount ESP if needed, read boot program(s) and unmount. */
805d7802caeSToomas Soome 	fp = fopen(MNTTAB, "r");
806d7802caeSToomas Soome 	if (fp == NULL) {
807d7802caeSToomas Soome 		perror("fopen");
808d7802caeSToomas Soome 		free(blkdev);
809d7802caeSToomas Soome 		return (rv);
810d7802caeSToomas Soome 	}
811d7802caeSToomas Soome 
812d7802caeSToomas Soome 	mpref.mnt_special = blkdev;
813d7802caeSToomas Soome 	ret = getmntany(fp, &mp, &mpref);
814d7802caeSToomas Soome 	(void) fclose(fp);
815d7802caeSToomas Soome 	if (ret == 0)
816d7802caeSToomas Soome 		path = mp.mnt_mountp;
817d7802caeSToomas Soome 	else
818d7802caeSToomas Soome 		path = stagefs_mount(blkdev, plist);
819d7802caeSToomas Soome 
820d7802caeSToomas Soome 	free(blkdev);
821d7802caeSToomas Soome 	if (path == NULL)
822d7802caeSToomas Soome 		return (rv);
823d7802caeSToomas Soome 
824d7802caeSToomas Soome 	if (asprintf(&file, "%s%s", path, plist->pl_device->stage.path) < 0) {
825d7802caeSToomas Soome 		return (rv);
826d7802caeSToomas Soome 	}
827d7802caeSToomas Soome 
828d7802caeSToomas Soome 	plist->pl_stage = calloc(1, sizeof (ib_bootblock_t));
829d7802caeSToomas Soome 	if (plist->pl_stage == NULL) {
830d7802caeSToomas Soome 		free(file);
831d7802caeSToomas Soome 		return (rv);
832d7802caeSToomas Soome 	}
833d7802caeSToomas Soome 	if (read_bootblock_from_file(file, plist->pl_stage) != BC_SUCCESS) {
834d7802caeSToomas Soome 		free(plist->pl_stage);
835d7802caeSToomas Soome 		plist->pl_stage = NULL;
836d7802caeSToomas Soome 	} else {
837d7802caeSToomas Soome 		rv = true;
838d7802caeSToomas Soome 	}
839d7802caeSToomas Soome 
840d7802caeSToomas Soome 	free(file);
841d7802caeSToomas Soome 	return (rv);
842d7802caeSToomas Soome }
843d7802caeSToomas Soome 
844d7802caeSToomas Soome static void
print_stage1_cb(struct partlist * plist)845d7802caeSToomas Soome print_stage1_cb(struct partlist *plist)
846d7802caeSToomas Soome {
847d7802caeSToomas Soome 	struct mboot *mbr;
848d7802caeSToomas Soome 	struct ipart *part;
849d7802caeSToomas Soome 	mbr_type_t type = MBR_TYPE_UNKNOWN;
850d7802caeSToomas Soome 	bool pmbr = false;
851d7802caeSToomas Soome 	char *label;
852d7802caeSToomas Soome 
853d7802caeSToomas Soome 	mbr = plist->pl_stage;
854d7802caeSToomas Soome 
855d7802caeSToomas Soome 	if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
856d7802caeSToomas Soome 		type = MBR_TYPE_GRUB1;
857d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
858d7802caeSToomas Soome 		type = MBR_TYPE_LOADER;
859d7802caeSToomas Soome 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
860d7802caeSToomas Soome 		type = MBR_TYPE_LOADER_JOYENT;
861d7802caeSToomas Soome 	}
862d7802caeSToomas Soome 
863d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
864d7802caeSToomas Soome 	for (int i = 0; i < FD_NUMPART; i++) {
865d7802caeSToomas Soome 		if (part[i].systid == EFI_PMBR)
866d7802caeSToomas Soome 			pmbr = true;
867d7802caeSToomas Soome 	}
868d7802caeSToomas Soome 
869d7802caeSToomas Soome 	if (plist->pl_type == IB_BBLK_MBR)
870d7802caeSToomas Soome 		label = pmbr ? "PMBR" : "MBR";
871d7802caeSToomas Soome 	else
872d7802caeSToomas Soome 		label = "VBR";
873d7802caeSToomas Soome 
874d7802caeSToomas Soome 	printf("%s block from %s:\n", label, plist->pl_devname);
875d7802caeSToomas Soome 
876d7802caeSToomas Soome 	switch (type) {
877d7802caeSToomas Soome 	case MBR_TYPE_UNKNOWN:
878d7802caeSToomas Soome 		printf("Format: unknown\n");
879d7802caeSToomas Soome 		break;
880d7802caeSToomas Soome 	case MBR_TYPE_GRUB1:
881d7802caeSToomas Soome 		printf("Format: grub1\n");
882d7802caeSToomas Soome 		break;
883d7802caeSToomas Soome 	case MBR_TYPE_LOADER:
884d7802caeSToomas Soome 		printf("Format: loader (illumos)\n");
885d7802caeSToomas Soome 		break;
886d7802caeSToomas Soome 	case MBR_TYPE_LOADER_JOYENT:
887d7802caeSToomas Soome 		printf("Format: loader (joyent)\n");
888d7802caeSToomas Soome 		break;
889d7802caeSToomas Soome 	}
890d7802caeSToomas Soome 
891d7802caeSToomas Soome 	printf("Signature: 0x%hx (%s)\n", mbr->signature,
892d7802caeSToomas Soome 	    mbr->signature == MBB_MAGIC ? "valid" : "invalid");
893d7802caeSToomas Soome 
894d7802caeSToomas Soome 	printf("UniqueMBRDiskSignature: %#lx\n",
895d7802caeSToomas Soome 	    *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
896d7802caeSToomas Soome 
897d7802caeSToomas Soome 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
898d7802caeSToomas Soome 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
899d7802caeSToomas Soome 
900d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_LBA: %llu\n",
901d7802caeSToomas Soome 		    *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
902d7802caeSToomas Soome 
903d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
904d7802caeSToomas Soome 		    *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
905d7802caeSToomas Soome 
906d7802caeSToomas Soome 		uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
907d7802caeSToomas Soome 		    uuid);
908d7802caeSToomas Soome 
909d7802caeSToomas Soome 		printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
910d7802caeSToomas Soome 	}
911d7802caeSToomas Soome 	printf("\n");
912d7802caeSToomas Soome }
913d7802caeSToomas Soome 
914d7802caeSToomas Soome static void
print_einfo_cb(struct partlist * plist)915d7802caeSToomas Soome print_einfo_cb(struct partlist *plist)
916d7802caeSToomas Soome {
917d7802caeSToomas Soome 	uint8_t flags = 0;
918d7802caeSToomas Soome 	ib_bootblock_t *bblock;
919d7802caeSToomas Soome 	bblk_einfo_t *einfo = NULL;
920d7802caeSToomas Soome 	const char *filepath;
921d7802caeSToomas Soome 
922d7802caeSToomas Soome 	/* No stage, get out. */
923d7802caeSToomas Soome 	bblock = plist->pl_stage;
924d7802caeSToomas Soome 	if (bblock == NULL)
925d7802caeSToomas Soome 		return;
926d7802caeSToomas Soome 
927d7802caeSToomas Soome 	if (plist->pl_device->stage.path == NULL)
928d7802caeSToomas Soome 		filepath = "";
929d7802caeSToomas Soome 	else
930d7802caeSToomas Soome 		filepath = plist->pl_device->stage.path;
931d7802caeSToomas Soome 
932d7802caeSToomas Soome 	printf("Boot block from %s:%s\n", plist->pl_devname, filepath);
933d7802caeSToomas Soome 
934d7802caeSToomas Soome 	if (bblock->extra != NULL)
935d7802caeSToomas Soome 		einfo = find_einfo(bblock->extra, bblock->extra_size);
936d7802caeSToomas Soome 
937d7802caeSToomas Soome 	if (einfo == NULL) {
938d7802caeSToomas Soome 		(void) fprintf(stderr,
939d7802caeSToomas Soome 		    gettext("No extended information found.\n\n"));
940d7802caeSToomas Soome 		return;
941d7802caeSToomas Soome 	}
942d7802caeSToomas Soome 
943d7802caeSToomas Soome 	/* Print the extended information. */
944d7802caeSToomas Soome 	if (strip)
945d7802caeSToomas Soome 		flags |= EINFO_EASY_PARSE;
946d7802caeSToomas Soome 	if (verbose_dump)
947d7802caeSToomas Soome 		flags |= EINFO_PRINT_HEADER;
948d7802caeSToomas Soome 
949d7802caeSToomas Soome 	print_einfo(flags, einfo, bblock->extra_size);
950d7802caeSToomas Soome 	printf("\n");
951d7802caeSToomas Soome }
952d7802caeSToomas Soome 
953d7802caeSToomas Soome static size_t
get_media_info(int fd)954d7802caeSToomas Soome get_media_info(int fd)
955d7802caeSToomas Soome {
956d7802caeSToomas Soome 	struct dk_minfo disk_info;
957d7802caeSToomas Soome 
958d7802caeSToomas Soome 	if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
959d7802caeSToomas Soome 		return (SECTOR_SIZE);
960d7802caeSToomas Soome 
961d7802caeSToomas Soome 	return (disk_info.dki_lbsize);
962d7802caeSToomas Soome }
963d7802caeSToomas Soome 
964d7802caeSToomas Soome static struct partlist *
partlist_alloc(void)965d7802caeSToomas Soome partlist_alloc(void)
966d7802caeSToomas Soome {
967d7802caeSToomas Soome 	struct partlist *pl;
968d7802caeSToomas Soome 
969d7802caeSToomas Soome 	if ((pl = calloc(1, sizeof (*pl))) == NULL) {
970d7802caeSToomas Soome 		perror("calloc");
971d7802caeSToomas Soome 		return (NULL);
972d7802caeSToomas Soome 	}
973d7802caeSToomas Soome 
974d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
975d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
976d7802caeSToomas Soome 		perror("calloc");
977d7802caeSToomas Soome 		free(pl);
978d7802caeSToomas Soome 		return (NULL);
979d7802caeSToomas Soome 	}
980d7802caeSToomas Soome 
981d7802caeSToomas Soome 	return (pl);
982d7802caeSToomas Soome }
983d7802caeSToomas Soome 
984d7802caeSToomas Soome static void
partlist_free(struct partlist * pl)985d7802caeSToomas Soome partlist_free(struct partlist *pl)
986d7802caeSToomas Soome {
987d7802caeSToomas Soome 	ib_bootblock_t *bblock;
988d7802caeSToomas Soome 	ib_device_t *device;
989d7802caeSToomas Soome 
990d7802caeSToomas Soome 	switch (pl->pl_type) {
991d7802caeSToomas Soome 	case IB_BBLK_MBR:
992d7802caeSToomas Soome 	case IB_BBLK_STAGE1:
993d7802caeSToomas Soome 		free(pl->pl_stage);
994d7802caeSToomas Soome 		break;
995d7802caeSToomas Soome 	default:
996d7802caeSToomas Soome 		if (pl->pl_stage != NULL) {
997d7802caeSToomas Soome 			bblock = pl->pl_stage;
998d7802caeSToomas Soome 			free(bblock->buf);
999d7802caeSToomas Soome 			free(bblock);
1000d7802caeSToomas Soome 		}
1001d7802caeSToomas Soome 	}
1002d7802caeSToomas Soome 
1003d7802caeSToomas Soome 	/* umount the stage fs. */
1004d7802caeSToomas Soome 	if (pl->pl_device->stage.mntpnt != NULL) {
1005d7802caeSToomas Soome 		if (umount(pl->pl_device->stage.mntpnt) == 0)
1006d7802caeSToomas Soome 			(void) rmdir(pl->pl_device->stage.mntpnt);
1007d7802caeSToomas Soome 		free(pl->pl_device->stage.mntpnt);
1008d7802caeSToomas Soome 	}
1009d7802caeSToomas Soome 	device = pl->pl_device;
1010d7802caeSToomas Soome 	free(device->target.path);
1011d7802caeSToomas Soome 	free(pl->pl_device);
1012d7802caeSToomas Soome 
1013d7802caeSToomas Soome 	free(pl->pl_src_data);
1014d7802caeSToomas Soome 	free(pl->pl_devname);
1015d7802caeSToomas Soome 	free(pl);
1016d7802caeSToomas Soome }
1017d7802caeSToomas Soome 
1018d7802caeSToomas Soome static bool
probe_fstyp(ib_data_t * data)1019d7802caeSToomas Soome probe_fstyp(ib_data_t *data)
1020d7802caeSToomas Soome {
1021d7802caeSToomas Soome 	fstyp_handle_t fhdl;
1022d7802caeSToomas Soome 	const char *fident;
1023d7802caeSToomas Soome 	char *ptr;
1024d7802caeSToomas Soome 	int fd;
1025d7802caeSToomas Soome 	bool rv = false;
1026d7802caeSToomas Soome 
1027d7802caeSToomas Soome 	/* Record partition id */
1028d7802caeSToomas Soome 	ptr = strrchr(data->target.path, 'p');
1029d7802caeSToomas Soome 	if (ptr == NULL)
1030d7802caeSToomas Soome 		ptr = strrchr(data->target.path, 's');
1031d7802caeSToomas Soome 	data->target.id = atoi(++ptr);
1032d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) == -1)
1033d7802caeSToomas Soome 		return (rv);
1034d7802caeSToomas Soome 
1035d7802caeSToomas Soome 	if (fstyp_init(fd, 0, NULL, &fhdl) != 0) {
1036d7802caeSToomas Soome 		(void) close(fd);
1037d7802caeSToomas Soome 		return (rv);
1038d7802caeSToomas Soome 	}
1039d7802caeSToomas Soome 
1040d7802caeSToomas Soome 	if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1041d7802caeSToomas Soome 		fstyp_fini(fhdl);
1042d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Failed to detect file "
1043d7802caeSToomas Soome 		    "system type\n"));
1044d7802caeSToomas Soome 		(void) close(fd);
1045d7802caeSToomas Soome 		return (rv);
1046d7802caeSToomas Soome 	}
1047d7802caeSToomas Soome 
1048d7802caeSToomas Soome 	rv = true;
1049d7802caeSToomas Soome 	if (strcmp(fident, MNTTYPE_ZFS) == 0)
1050d7802caeSToomas Soome 		data->target.fstype = IB_FS_ZFS;
1051d7802caeSToomas Soome 	else if (strcmp(fident, MNTTYPE_UFS) == 0) {
1052d7802caeSToomas Soome 		data->target.fstype = IB_FS_UFS;
1053d7802caeSToomas Soome 	} else if (strcmp(fident, MNTTYPE_PCFS) == 0) {
1054d7802caeSToomas Soome 		data->target.fstype = IB_FS_PCFS;
1055d8d05a42SToomas Soome 		/* with pcfs we always write MBR */
1056d8d05a42SToomas Soome 		force_mbr = true;
1057d8d05a42SToomas Soome 		write_mbr = true;
1058d7802caeSToomas Soome 	} else {
1059d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("File system %s is not "
1060d7802caeSToomas Soome 		    "supported by loader\n"), fident);
1061d7802caeSToomas Soome 		rv = false;
1062d7802caeSToomas Soome 	}
1063d7802caeSToomas Soome 	fstyp_fini(fhdl);
1064d7802caeSToomas Soome 	(void) close(fd);
1065d7802caeSToomas Soome 	return (rv);
1066d7802caeSToomas Soome }
1067d7802caeSToomas Soome 
1068d7802caeSToomas Soome static bool
get_slice(ib_data_t * data,struct partlist * pl,struct dk_gpt * vtoc,uint16_t tag)1069d7802caeSToomas Soome get_slice(ib_data_t *data, struct partlist *pl, struct dk_gpt *vtoc,
1070d7802caeSToomas Soome     uint16_t tag)
1071d7802caeSToomas Soome {
1072d7802caeSToomas Soome 	uint_t i;
1073d7802caeSToomas Soome 	ib_device_t *device = pl->pl_device;
1074d7802caeSToomas Soome 	char *path, *ptr;
1075d7802caeSToomas Soome 
1076d7802caeSToomas Soome 	if (tag != V_BOOT && tag != V_SYSTEM)
1077d7802caeSToomas Soome 		return (false);
1078d7802caeSToomas Soome 
1079d7802caeSToomas Soome 	for (i = 0; i < vtoc->efi_nparts; i++) {
1080d7802caeSToomas Soome 		if (vtoc->efi_parts[i].p_tag == tag) {
1081d7802caeSToomas Soome 			if ((path = strdup(data->target.path)) == NULL) {
1082d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1083d7802caeSToomas Soome 				return (false);
1084d7802caeSToomas Soome 			}
1085d7802caeSToomas Soome 			ptr = strrchr(path, 's');
1086d7802caeSToomas Soome 			ptr++;
1087d7802caeSToomas Soome 			*ptr = '\0';
1088d7802caeSToomas Soome 			(void) asprintf(&ptr, "%s%d", path, i);
1089d7802caeSToomas Soome 			free(path);
1090d7802caeSToomas Soome 			if (ptr == NULL) {
1091d7802caeSToomas Soome 				perror(gettext("Memory allocation failure"));
1092d7802caeSToomas Soome 				return (false);
1093d7802caeSToomas Soome 			}
1094d7802caeSToomas Soome 			pl->pl_devname = ptr;
1095d7802caeSToomas Soome 			device->stage.id = i;
1096d7802caeSToomas Soome 			device->stage.devtype = IB_DEV_EFI;
1097d7802caeSToomas Soome 			switch (vtoc->efi_parts[i].p_tag) {
1098d7802caeSToomas Soome 			case V_BOOT:
1099d7802caeSToomas Soome 				device->stage.fstype = IB_FS_NONE;
1100d7802caeSToomas Soome 				/* leave sector 0 for VBR */
1101d7802caeSToomas Soome 				device->stage.offset = 1;
1102d7802caeSToomas Soome 				break;
1103d7802caeSToomas Soome 			case V_SYSTEM:
1104d7802caeSToomas Soome 				device->stage.fstype = IB_FS_PCFS;
1105d7802caeSToomas Soome 				break;
1106d7802caeSToomas Soome 			}
1107d7802caeSToomas Soome 			device->stage.tag = vtoc->efi_parts[i].p_tag;
1108d7802caeSToomas Soome 			device->stage.start = vtoc->efi_parts[i].p_start;
1109d7802caeSToomas Soome 			device->stage.size = vtoc->efi_parts[i].p_size;
1110d7802caeSToomas Soome 			break;
1111d7802caeSToomas Soome 		}
1112d7802caeSToomas Soome 	}
1113d7802caeSToomas Soome 	return (true);
1114d7802caeSToomas Soome }
1115d7802caeSToomas Soome 
1116d7802caeSToomas Soome static bool
allocate_slice(ib_data_t * data,struct dk_gpt * vtoc,uint16_t tag,struct partlist ** plp)1117d7802caeSToomas Soome allocate_slice(ib_data_t *data, struct dk_gpt *vtoc, uint16_t tag,
1118d7802caeSToomas Soome     struct partlist **plp)
1119d7802caeSToomas Soome {
1120d7802caeSToomas Soome 	struct partlist *pl;
1121d7802caeSToomas Soome 
1122d7802caeSToomas Soome 	*plp = NULL;
1123d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1124d7802caeSToomas Soome 		return (false);
1125d7802caeSToomas Soome 
1126d7802caeSToomas Soome 	pl->pl_device = calloc(1, sizeof (*pl->pl_device));
1127d7802caeSToomas Soome 	if (pl->pl_device == NULL) {
1128d7802caeSToomas Soome 		perror("calloc");
1129d7802caeSToomas Soome 		partlist_free(pl);
1130d7802caeSToomas Soome 		return (false);
1131d7802caeSToomas Soome 	}
1132d7802caeSToomas Soome 	if (!get_slice(data, pl, vtoc, tag)) {
1133d7802caeSToomas Soome 		partlist_free(pl);
1134d7802caeSToomas Soome 		return (false);
1135d7802caeSToomas Soome 	}
1136d7802caeSToomas Soome 
1137d7802caeSToomas Soome 	/* tag was not found */
1138d7802caeSToomas Soome 	if (pl->pl_devname == NULL)
1139d7802caeSToomas Soome 		partlist_free(pl);
1140d7802caeSToomas Soome 	else
1141d7802caeSToomas Soome 		*plp = pl;
1142d7802caeSToomas Soome 
1143d7802caeSToomas Soome 	return (true);
1144d7802caeSToomas Soome }
1145d7802caeSToomas Soome 
1146d7802caeSToomas Soome static bool
probe_gpt(ib_data_t * data)1147d7802caeSToomas Soome probe_gpt(ib_data_t *data)
1148d7802caeSToomas Soome {
1149d7802caeSToomas Soome 	struct partlist *pl;
1150d7802caeSToomas Soome 	struct dk_gpt *vtoc;
1151d7802caeSToomas Soome 	ib_device_t *device;
1152d7802caeSToomas Soome 	int slice, fd;
1153d7802caeSToomas Soome 	bool rv = false;
1154d7802caeSToomas Soome 
1155d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1156d7802caeSToomas Soome 		return (rv);
1157d7802caeSToomas Soome 
1158d7802caeSToomas Soome 	slice = efi_alloc_and_read(fd, &vtoc);
1159d7802caeSToomas Soome 	(void) close(fd);
1160d7802caeSToomas Soome 	if (slice < 0)
1161d7802caeSToomas Soome 		return (rv);
1162d7802caeSToomas Soome 
1163d7802caeSToomas Soome 	data->device.devtype = IB_DEV_EFI;
1164d7802caeSToomas Soome 	data->target.start = vtoc->efi_parts[slice].p_start;
1165d7802caeSToomas Soome 	data->target.size = vtoc->efi_parts[slice].p_size;
1166d7802caeSToomas Soome 
1167d7802caeSToomas Soome 	/* Always update PMBR. */
1168d8d05a42SToomas Soome 	force_mbr = true;
1169d8d05a42SToomas Soome 	write_mbr = true;
1170d7802caeSToomas Soome 
1171d7802caeSToomas Soome 	/*
1172d7802caeSToomas Soome 	 * With GPT we can have boot partition and ESP.
1173d7802caeSToomas Soome 	 * Boot partition can have both stage 1 and stage 2.
1174d7802caeSToomas Soome 	 */
1175d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1176d7802caeSToomas Soome 		goto done;
1177d7802caeSToomas Soome 	if (pl != NULL) {
1178d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1179d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1180d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1181d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1182d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1183d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1184d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1185d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1186d7802caeSToomas Soome 	} else if (data->target.fstype != IB_FS_ZFS) {
1187d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Booting %s from EFI "
1188d7802caeSToomas Soome 		    "labeled disks requires the boot partition.\n"),
1189d7802caeSToomas Soome 		    data->target.fstype == IB_FS_UFS?
1190d7802caeSToomas Soome 		    MNTTYPE_UFS : MNTTYPE_PCFS);
1191d7802caeSToomas Soome 		goto done;
1192d7802caeSToomas Soome 	}
1193d7802caeSToomas Soome 	/* Add stage 2 */
1194d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_BOOT, &pl))
1195d7802caeSToomas Soome 		goto done;
1196d7802caeSToomas Soome 	if (pl != NULL) {
1197d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1198d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1199d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1200d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1201d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1202d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1203d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1204d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1205d7802caeSToomas Soome 	}
1206d7802caeSToomas Soome 
1207d7802caeSToomas Soome 	/* ESP can have 32- and 64-bit boot code. */
1208d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1209d7802caeSToomas Soome 		goto done;
1210d7802caeSToomas Soome 	if (pl != NULL) {
1211d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTIA32;
1212d7802caeSToomas Soome 		pl->pl_src_name = efi32;
1213d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1214d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1215d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1216d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1217d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1218d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1219d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1220d7802caeSToomas Soome 	}
1221d7802caeSToomas Soome 	if (!allocate_slice(data, vtoc, V_SYSTEM, &pl))
1222d7802caeSToomas Soome 		goto done;
1223d7802caeSToomas Soome 	if (pl != NULL) {
1224d7802caeSToomas Soome 		pl->pl_device->stage.path = "/EFI/Boot/" BOOTX64;
1225d7802caeSToomas Soome 		pl->pl_src_name = efi64;
1226d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_EFI;
1227d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1228d7802caeSToomas Soome 		pl->pl_cb.install = install_esp_cb;
1229d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_esp_cb;
1230d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1231d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1232d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1233d7802caeSToomas Soome 	}
1234d7802caeSToomas Soome 
1235d7802caeSToomas Soome 	/* add stage for our target file system slice */
1236d7802caeSToomas Soome 	pl = partlist_alloc();
1237d7802caeSToomas Soome 	if (pl == NULL)
1238d7802caeSToomas Soome 		goto done;
1239d7802caeSToomas Soome 
1240d7802caeSToomas Soome 	device = pl->pl_device;
1241d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1242d7802caeSToomas Soome 	if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1243d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1244d7802caeSToomas Soome 		partlist_free(pl);
1245d7802caeSToomas Soome 		goto done;
1246d7802caeSToomas Soome 	}
1247d7802caeSToomas Soome 
1248d7802caeSToomas Soome 	device->stage.id = slice;
1249d7802caeSToomas Soome 	device->stage.start = vtoc->efi_parts[slice].p_start;
1250d7802caeSToomas Soome 	device->stage.size = vtoc->efi_parts[slice].p_size;
1251d7802caeSToomas Soome 
1252d7802caeSToomas Soome 	/* ZFS and UFS can have stage1 in boot area. */
1253d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS ||
1254d7802caeSToomas Soome 	    data->target.fstype == IB_FS_UFS) {
1255d7802caeSToomas Soome 		pl->pl_src_name = stage1;
1256d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE1;
1257d7802caeSToomas Soome 		pl->pl_cb.compare = compare_stage1_cb;
1258d7802caeSToomas Soome 		pl->pl_cb.install = install_stage1_cb;
1259d7802caeSToomas Soome 		pl->pl_cb.read = read_stage1_cb;
1260d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1261d7802caeSToomas Soome 		pl->pl_cb.print = print_stage1_cb;
1262d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1263d7802caeSToomas Soome 	}
1264d7802caeSToomas Soome 
1265d7802caeSToomas Soome 	if (data->target.fstype == IB_FS_ZFS) {
1266d7802caeSToomas Soome 		pl = partlist_alloc();
1267d7802caeSToomas Soome 		if (pl == NULL)
1268d7802caeSToomas Soome 			goto done;
1269d7802caeSToomas Soome 
1270d7802caeSToomas Soome 		device = pl->pl_device;
1271d7802caeSToomas Soome 		device->stage.devtype = data->device.devtype;
1272d7802caeSToomas Soome 
1273d7802caeSToomas Soome 		if ((pl->pl_devname = strdup(data->target.path)) == NULL) {
1274d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1275d7802caeSToomas Soome 			goto done;
1276d7802caeSToomas Soome 		}
1277d7802caeSToomas Soome 
1278d7802caeSToomas Soome 		device->stage.id = slice;
1279d7802caeSToomas Soome 		device->stage.start = vtoc->efi_parts[slice].p_start;
1280d7802caeSToomas Soome 		device->stage.size = vtoc->efi_parts[slice].p_size;
1281d7802caeSToomas Soome 
1282*0cc97882SToomas Soome 		device->stage.offset = BBLK_ZFS_BLK_OFF / sector_size;
1283d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1284d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1285d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1286d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1287d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1288d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1289d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1290d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1291d7802caeSToomas Soome 	}
1292d7802caeSToomas Soome 	rv = true;
1293d7802caeSToomas Soome done:
1294d7802caeSToomas Soome 	efi_free(vtoc);
1295d7802caeSToomas Soome 	return (rv);
1296d7802caeSToomas Soome }
1297d7802caeSToomas Soome 
1298d7802caeSToomas Soome static bool
get_start_sector(ib_data_t * data,struct extpartition * v_part,diskaddr_t * start)1299d7802caeSToomas Soome get_start_sector(ib_data_t *data, struct extpartition *v_part,
1300d7802caeSToomas Soome     diskaddr_t *start)
1301d7802caeSToomas Soome {
1302d7802caeSToomas Soome 	struct partlist *pl;
1303d7802caeSToomas Soome 	struct mboot *mbr;
1304d7802caeSToomas Soome 	struct ipart *part;
1305d7802caeSToomas Soome 	struct part_info dkpi;
1306d7802caeSToomas Soome 	struct extpart_info edkpi;
1307d7802caeSToomas Soome 	uint32_t secnum, numsec;
1308d7802caeSToomas Soome 	ext_part_t *epp;
1309d7802caeSToomas Soome 	ushort_t i;
1310d7802caeSToomas Soome 	int fd, rval, pno;
1311d7802caeSToomas Soome 
1312d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1313d7802caeSToomas Soome 		return (false);
1314d7802caeSToomas Soome 
1315d7802caeSToomas Soome 	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
1316d7802caeSToomas Soome 		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
1317d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("cannot get the "
1318d7802caeSToomas Soome 			    "slice information of the disk\n"));
1319d7802caeSToomas Soome 			(void) close(fd);
1320d7802caeSToomas Soome 			return (false);
1321d7802caeSToomas Soome 		} else {
1322d7802caeSToomas Soome 			edkpi.p_start = dkpi.p_start;
1323d7802caeSToomas Soome 			edkpi.p_length = dkpi.p_length;
1324d7802caeSToomas Soome 		}
13250c946d80SToomas Soome 	}
13260c946d80SToomas Soome 	(void) close(fd);
1327d7802caeSToomas Soome 
1328d7802caeSToomas Soome 	/* Set target file system start and size */
1329d7802caeSToomas Soome 	data->target.start = edkpi.p_start;
1330d7802caeSToomas Soome 	data->target.size = edkpi.p_length;
1331d7802caeSToomas Soome 
1332d7802caeSToomas Soome 	/* This is our MBR partition start. */
1333d7802caeSToomas Soome 	edkpi.p_start -= v_part->p_start;
1334d7802caeSToomas Soome 
1335d7802caeSToomas Soome 	/* Head is always MBR */
1336d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1337d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1338d7802caeSToomas Soome 		return (false);
1339d7802caeSToomas Soome 
1340d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1341d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
1342d7802caeSToomas Soome 
1343d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1344d7802caeSToomas Soome 		if (part[i].relsect == edkpi.p_start) {
1345d7802caeSToomas Soome 			*start = part[i].relsect;
1346d7802caeSToomas Soome 			return (true);
1347d7802caeSToomas Soome 		}
1348d7802caeSToomas Soome 	}
1349d7802caeSToomas Soome 
1350d7802caeSToomas Soome 	rval = libfdisk_init(&epp, pl->pl_devname, part, FDISK_READ_DISK);
1351d7802caeSToomas Soome 	if (rval != FDISK_SUCCESS) {
1352d7802caeSToomas Soome 		switch (rval) {
1353d7802caeSToomas Soome 			/*
1354d7802caeSToomas Soome 			 * The first 3 cases are not an error per-se, just that
1355d7802caeSToomas Soome 			 * there is no Solaris logical partition
1356d7802caeSToomas Soome 			 */
1357d7802caeSToomas Soome 			case FDISK_EBADLOGDRIVE:
1358d7802caeSToomas Soome 			case FDISK_ENOLOGDRIVE:
1359d7802caeSToomas Soome 			case FDISK_EBADMAGIC:
1360d7802caeSToomas Soome 				(void) fprintf(stderr, gettext("Solaris "
1361d7802caeSToomas Soome 				    "partition not found. "
1362d7802caeSToomas Soome 				    "Aborting operation. %d\n"), rval);
1363d7802caeSToomas Soome 				return (false);
1364d7802caeSToomas Soome 			case FDISK_ENOVGEOM:
1365d7802caeSToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
1366d7802caeSToomas Soome 				    "virtual geometry\n"));
1367d7802caeSToomas Soome 				return (false);
1368d7802caeSToomas Soome 			case FDISK_ENOPGEOM:
1369d7802caeSToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
1370d7802caeSToomas Soome 				    "physical geometry\n"));
1371d7802caeSToomas Soome 				return (false);
1372d7802caeSToomas Soome 			case FDISK_ENOLGEOM:
1373d7802caeSToomas Soome 				(void) fprintf(stderr, gettext("Could not get "
1374d7802caeSToomas Soome 				    "label geometry\n"));
1375d7802caeSToomas Soome 				return (false);
1376d7802caeSToomas Soome 			default:
1377d7802caeSToomas Soome 				(void) fprintf(stderr, gettext("Failed to "
1378d7802caeSToomas Soome 				    "initialize libfdisk.\n"));
1379d7802caeSToomas Soome 				return (false);
1380d7802caeSToomas Soome 		}
1381d7802caeSToomas Soome 	}
1382d7802caeSToomas Soome 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
1383d7802caeSToomas Soome 	libfdisk_fini(&epp);
1384d7802caeSToomas Soome 	if (rval != FDISK_SUCCESS) {
1385d7802caeSToomas Soome 		/* No solaris logical partition */
1386d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("Solaris partition not found. "
1387d7802caeSToomas Soome 		    "Aborting operation.\n"));
1388d7802caeSToomas Soome 		return (false);
1389d7802caeSToomas Soome 	}
1390d7802caeSToomas Soome 	*start = secnum;
1391d7802caeSToomas Soome 	return (true);
1392d7802caeSToomas Soome }
1393d7802caeSToomas Soome 
1394d7802caeSToomas Soome /*
1395d7802caeSToomas Soome  * On x86 the VTOC table is inside MBR partition and to get
1396d7802caeSToomas Soome  * absolute sectors, we need to add MBR partition start to VTOC slice start.
1397d7802caeSToomas Soome  */
1398d7802caeSToomas Soome static bool
probe_vtoc(ib_data_t * data)1399d7802caeSToomas Soome probe_vtoc(ib_data_t *data)
1400d7802caeSToomas Soome {
1401d7802caeSToomas Soome 	struct partlist *pl;
1402d7802caeSToomas Soome 	struct extvtoc exvtoc;
1403d7802caeSToomas Soome 	ib_device_t *device;
1404d7802caeSToomas Soome 	char *path, *ptr;
1405d7802caeSToomas Soome 	ushort_t i;
1406d7802caeSToomas Soome 	int slice, fd;
1407d7802caeSToomas Soome 	diskaddr_t start;
1408d7802caeSToomas Soome 	bool rv;
1409d7802caeSToomas Soome 
1410d7802caeSToomas Soome 	rv = false;
1411d7802caeSToomas Soome 
1412d7802caeSToomas Soome 	if ((fd = open_device(data->target.path)) < 0)
1413d7802caeSToomas Soome 		return (rv);
1414d7802caeSToomas Soome 
1415d7802caeSToomas Soome 	slice = read_extvtoc(fd, &exvtoc);
1416d7802caeSToomas Soome 	(void) close(fd);
1417d7802caeSToomas Soome 	if (slice < 0)
1418d7802caeSToomas Soome 		return (rv);
1419d7802caeSToomas Soome 	data->device.devtype = IB_DEV_VTOC;
1420d7802caeSToomas Soome 
1421d7802caeSToomas Soome 	if (!get_start_sector(data, exvtoc.v_part + slice, &start))
1422d7802caeSToomas Soome 		return (rv);
1423d7802caeSToomas Soome 
1424d7802caeSToomas Soome 	if (exvtoc.v_part[slice].p_tag == V_BACKUP) {
1425d7802caeSToomas Soome 		/*
1426d7802caeSToomas Soome 		 * NOTE: we could relax there and allow zfs boot on
1427d7802caeSToomas Soome 		 * slice 2, but lets keep traditional limits.
1428d7802caeSToomas Soome 		 */
1429d7802caeSToomas Soome 		(void) fprintf(stderr, gettext(
1430d7802caeSToomas Soome 		    "raw device must be a root slice (not backup)\n"));
1431d7802caeSToomas Soome 		return (rv);
1432d7802caeSToomas Soome 	}
1433d7802caeSToomas Soome 
1434d7802caeSToomas Soome 	if ((path = strdup(data->target.path)) == NULL) {
1435d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1436d7802caeSToomas Soome 		return (false);
1437d7802caeSToomas Soome 	}
1438d7802caeSToomas Soome 
1439d7802caeSToomas Soome 	data->target.start = start + exvtoc.v_part[slice].p_start;
1440d7802caeSToomas Soome 	data->target.size = exvtoc.v_part[slice].p_size;
1441d7802caeSToomas Soome 
1442d7802caeSToomas Soome 	/* Search for boot slice. */
1443d7802caeSToomas Soome 	for (i = 0; i < exvtoc.v_nparts; i++) {
1444d7802caeSToomas Soome 		if (exvtoc.v_part[i].p_tag == V_BOOT)
1445d7802caeSToomas Soome 			break;
1446d7802caeSToomas Soome 	}
1447d7802caeSToomas Soome 
1448d7802caeSToomas Soome 	if (i == exvtoc.v_nparts ||
1449d7802caeSToomas Soome 	    exvtoc.v_part[i].p_size == 0) {
1450d7802caeSToomas Soome 		/* fall back to slice V_BACKUP */
1451d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1452d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_tag == V_BACKUP)
1453d7802caeSToomas Soome 				break;
1454d7802caeSToomas Soome 		}
1455d7802caeSToomas Soome 		/* Still nothing? Error out. */
1456d7802caeSToomas Soome 		if (i == exvtoc.v_nparts ||
1457d7802caeSToomas Soome 		    exvtoc.v_part[i].p_size == 0) {
1458d7802caeSToomas Soome 			free(path);
1459d7802caeSToomas Soome 			return (false);
1460d7802caeSToomas Soome 		}
1461d7802caeSToomas Soome 	}
1462d7802caeSToomas Soome 
1463d7802caeSToomas Soome 	/* Create path. */
1464d7802caeSToomas Soome 	ptr = strrchr(path, 's');
1465d7802caeSToomas Soome 	ptr++;
1466d7802caeSToomas Soome 	*ptr = '\0';
1467d7802caeSToomas Soome 	(void) asprintf(&ptr, "%s%d", path, i);
1468d7802caeSToomas Soome 	free(path);
1469d7802caeSToomas Soome 	if (ptr == NULL) {
1470d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1471d7802caeSToomas Soome 		return (false);
1472d7802caeSToomas Soome 	}
1473d7802caeSToomas Soome 
1474d7802caeSToomas Soome 	pl = partlist_alloc();
1475d7802caeSToomas Soome 	if (pl == NULL) {
1476d7802caeSToomas Soome 		free(ptr);
1477d7802caeSToomas Soome 		return (false);
1478d7802caeSToomas Soome 	}
1479d7802caeSToomas Soome 	pl->pl_devname = ptr;
1480d7802caeSToomas Soome 	device = pl->pl_device;
1481d7802caeSToomas Soome 	device->stage.devtype = data->device.devtype;
1482d7802caeSToomas Soome 	device->stage.id = i;
1483d7802caeSToomas Soome 	device->stage.tag = exvtoc.v_part[i].p_tag;
1484d7802caeSToomas Soome 	device->stage.start = start + exvtoc.v_part[i].p_start;
1485d7802caeSToomas Soome 	device->stage.size = exvtoc.v_part[i].p_size;
1486d7802caeSToomas Soome 
1487d7802caeSToomas Soome 	/* Fix size if this slice is in fact V_BACKUP */
1488d7802caeSToomas Soome 	if (exvtoc.v_part[i].p_tag == V_BACKUP) {
1489d7802caeSToomas Soome 		for (i = 0; i < exvtoc.v_nparts; i++) {
1490d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start == 0)
1491d7802caeSToomas Soome 				continue;
1492d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_size == 0)
1493d7802caeSToomas Soome 				continue;
1494d7802caeSToomas Soome 			if (exvtoc.v_part[i].p_start <
1495d7802caeSToomas Soome 			    device->stage.size)
1496d7802caeSToomas Soome 				device->stage.size =
1497d7802caeSToomas Soome 				    exvtoc.v_part[i].p_start;
1498d7802caeSToomas Soome 		}
1499d7802caeSToomas Soome 	}
1500d7802caeSToomas Soome 
1501d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1502d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1503d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1504d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1505d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1506d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1507d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1508d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1509d7802caeSToomas Soome 
1510d7802caeSToomas Soome 	/* Create instance for stage 2 */
1511d7802caeSToomas Soome 	pl = partlist_alloc();
1512d7802caeSToomas Soome 	if (pl == NULL) {
1513d7802caeSToomas Soome 		free(ptr);
1514d7802caeSToomas Soome 		return (false);
1515d7802caeSToomas Soome 	}
1516d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1517d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1518d7802caeSToomas Soome 		partlist_free(pl);
1519d7802caeSToomas Soome 		return (false);
1520d7802caeSToomas Soome 	}
1521d7802caeSToomas Soome 	pl->pl_device->stage.devtype = data->device.devtype;
1522d7802caeSToomas Soome 	pl->pl_device->stage.id = device->stage.id;
1523d7802caeSToomas Soome 	pl->pl_device->stage.offset = BBLK_BLKLIST_OFF;
1524d7802caeSToomas Soome 	pl->pl_device->stage.tag = device->stage.tag;
1525d7802caeSToomas Soome 	pl->pl_device->stage.start = device->stage.start;
1526d7802caeSToomas Soome 	pl->pl_device->stage.size = device->stage.size;
1527d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1528d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1529d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1530d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1531d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1532d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1533d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1534d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1535d7802caeSToomas Soome 
1536d7802caeSToomas Soome 	/* And we are done. */
1537d7802caeSToomas Soome 	rv = true;
1538d7802caeSToomas Soome 	return (rv);
1539d7802caeSToomas Soome }
1540d7802caeSToomas Soome 
1541d7802caeSToomas Soome static bool
probe_mbr(ib_data_t * data)1542d7802caeSToomas Soome probe_mbr(ib_data_t *data)
1543d7802caeSToomas Soome {
1544d7802caeSToomas Soome 	struct partlist *pl;
1545d7802caeSToomas Soome 	struct ipart *part;
1546d7802caeSToomas Soome 	struct mboot *mbr;
1547d7802caeSToomas Soome 	ib_device_t *device;
1548d7802caeSToomas Soome 	char *path, *ptr;
1549d7802caeSToomas Soome 	int i, rv;
1550d7802caeSToomas Soome 
1551d7802caeSToomas Soome 	data->device.devtype = IB_DEV_MBR;
1552d7802caeSToomas Soome 
1553d7802caeSToomas Soome 	/* Head is always MBR */
1554d7802caeSToomas Soome 	pl = STAILQ_FIRST(data->plist);
1555d7802caeSToomas Soome 	if (!read_stage1_cb(pl))
1556d7802caeSToomas Soome 		return (false);
1557d7802caeSToomas Soome 
1558d7802caeSToomas Soome 	mbr = (struct mboot *)pl->pl_stage;
1559d7802caeSToomas Soome 	part = (struct ipart *)mbr->parts;
1560d7802caeSToomas Soome 
1561d7802caeSToomas Soome 	/* Set target file system start and size */
1562d7802caeSToomas Soome 	data->target.start = part[data->target.id - 1].relsect;
1563d7802caeSToomas Soome 	data->target.size = part[data->target.id - 1].numsect;
1564d7802caeSToomas Soome 
1565d7802caeSToomas Soome 	/* Use X86BOOT partition if we have one. */
1566d7802caeSToomas Soome 	for (i = 0; i < FD_NUMPART; i++) {
1567d7802caeSToomas Soome 		if (part[i].systid == X86BOOT)
1568d7802caeSToomas Soome 			break;
1569d7802caeSToomas Soome 	}
1570d7802caeSToomas Soome 
1571d7802caeSToomas Soome 	/* Keep device name of whole disk device. */
1572d7802caeSToomas Soome 	path = (char *)pl->pl_devname;
1573d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1574d7802caeSToomas Soome 		return (false);
1575d7802caeSToomas Soome 	device = pl->pl_device;
1576d7802caeSToomas Soome 
1577d7802caeSToomas Soome 	/*
1578d7802caeSToomas Soome 	 * No X86BOOT, try to use space between MBR and first
1579d7802caeSToomas Soome 	 * partition.
1580d7802caeSToomas Soome 	 */
1581d7802caeSToomas Soome 	if (i == FD_NUMPART) {
1582d7802caeSToomas Soome 		pl->pl_devname = strdup(path);
1583d7802caeSToomas Soome 		if (pl->pl_devname == NULL) {
1584d7802caeSToomas Soome 			perror(gettext("Memory allocation failure"));
1585d7802caeSToomas Soome 			partlist_free(pl);
1586d7802caeSToomas Soome 			return (false);
1587d7802caeSToomas Soome 		}
1588d7802caeSToomas Soome 		device->stage.id = 0;
1589d7802caeSToomas Soome 		device->stage.devtype = IB_DEV_MBR;
1590d7802caeSToomas Soome 		device->stage.fstype = IB_FS_NONE;
1591d7802caeSToomas Soome 		device->stage.start = 0;
1592d7802caeSToomas Soome 		device->stage.size = part[0].relsect;
1593d7802caeSToomas Soome 		device->stage.offset = BBLK_BLKLIST_OFF;
1594d7802caeSToomas Soome 		pl->pl_src_name = stage2;
1595d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_STAGE2;
1596d7802caeSToomas Soome 		pl->pl_cb.compare = compare_einfo_cb;
1597d7802caeSToomas Soome 		pl->pl_cb.install = install_stage2_cb;
1598d7802caeSToomas Soome 		pl->pl_cb.read = read_stage2_cb;
1599d7802caeSToomas Soome 		pl->pl_cb.read_bbl = read_stage2_file_cb;
1600d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1601d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1602d7802caeSToomas Soome 
1603d7802caeSToomas Soome 		/* We have MBR for stage1 and gap for stage2, we are done. */
1604d7802caeSToomas Soome 		return (true);
1605d7802caeSToomas Soome 	}
1606d7802caeSToomas Soome 
1607d7802caeSToomas Soome 	if ((path = strdup(path)) == NULL) {
1608d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1609d7802caeSToomas Soome 		partlist_free(pl);
1610d7802caeSToomas Soome 		return (false);
1611d7802caeSToomas Soome 	}
1612d7802caeSToomas Soome 	ptr = strrchr(path, 'p');
1613d7802caeSToomas Soome 	ptr++;
1614d7802caeSToomas Soome 	*ptr = '\0';
1615d7802caeSToomas Soome 	/* partitions are p1..p4 */
1616d7802caeSToomas Soome 	rv = asprintf(&ptr, "%s%d", path, i + 1);
1617d7802caeSToomas Soome 	free(path);
1618d7802caeSToomas Soome 	if (rv < 0) {
1619d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1620d7802caeSToomas Soome 		partlist_free(pl);
1621d7802caeSToomas Soome 		return (false);
1622d7802caeSToomas Soome 	}
1623d7802caeSToomas Soome 	pl->pl_devname = ptr;
1624d7802caeSToomas Soome 	device->stage.id = i + 1;
1625d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1626d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1627d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1628d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1629d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1630d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE1;
1631d7802caeSToomas Soome 	pl->pl_cb.compare = compare_stage1_cb;
1632d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1633d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1634d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1635d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1636d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1637d7802caeSToomas Soome 
1638d7802caeSToomas Soome 	pl = partlist_alloc();
1639d7802caeSToomas Soome 	if (pl == NULL)
1640d7802caeSToomas Soome 		return (false);
1641d7802caeSToomas Soome 	device = pl->pl_device;
1642d7802caeSToomas Soome 	pl->pl_devname = strdup(ptr);
1643d7802caeSToomas Soome 	if (pl->pl_devname == NULL) {
1644d7802caeSToomas Soome 		perror(gettext("Memory allocation failure"));
1645d7802caeSToomas Soome 		partlist_free(pl);
1646d7802caeSToomas Soome 		return (false);
1647d7802caeSToomas Soome 	}
1648d7802caeSToomas Soome 	device->stage.id = i + 1;
1649d7802caeSToomas Soome 	device->stage.devtype = IB_DEV_MBR;
1650d7802caeSToomas Soome 	device->stage.fstype = IB_FS_NONE;
1651d7802caeSToomas Soome 	device->stage.start = part[i].relsect;
1652d7802caeSToomas Soome 	device->stage.size = part[i].numsect;
1653d7802caeSToomas Soome 	device->stage.offset = 1;
1654d7802caeSToomas Soome 	/* This is boot partition */
1655d7802caeSToomas Soome 	device->stage.tag = V_BOOT;
1656d7802caeSToomas Soome 	pl->pl_src_name = stage2;
1657d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_STAGE2;
1658d7802caeSToomas Soome 	pl->pl_cb.compare = compare_einfo_cb;
1659d7802caeSToomas Soome 	pl->pl_cb.install = install_stage2_cb;
1660d7802caeSToomas Soome 	pl->pl_cb.read = read_stage2_cb;
1661d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage2_file_cb;
1662d7802caeSToomas Soome 	pl->pl_cb.print = print_einfo_cb;
1663d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1664d7802caeSToomas Soome 
1665d7802caeSToomas Soome 	return (true);
1666d7802caeSToomas Soome }
1667d7802caeSToomas Soome 
1668d7802caeSToomas Soome static bool
probe_device(ib_data_t * data,const char * dev)1669d7802caeSToomas Soome probe_device(ib_data_t *data, const char *dev)
1670d7802caeSToomas Soome {
1671d7802caeSToomas Soome 	struct partlist *pl;
1672d7802caeSToomas Soome 	struct stat sb;
1673d7802caeSToomas Soome 	const char *ptr;
1674d7802caeSToomas Soome 	char *p0;
1675d7802caeSToomas Soome 	int fd, len;
1676d7802caeSToomas Soome 
1677d7802caeSToomas Soome 	if (dev == NULL)
1678d7802caeSToomas Soome 		return (NULL);
1679d7802caeSToomas Soome 
1680d7802caeSToomas Soome 	len = strlen(dev);
1681d7802caeSToomas Soome 
1682d7802caeSToomas Soome 	if ((pl = partlist_alloc()) == NULL)
1683d7802caeSToomas Soome 		return (false);
1684d7802caeSToomas Soome 
1685d7802caeSToomas Soome 	if (stat(dev, &sb) == -1) {
1686d7802caeSToomas Soome 		perror("stat");
1687d7802caeSToomas Soome 		partlist_free(pl);
1688d7802caeSToomas Soome 		return (false);
1689d7802caeSToomas Soome 	}
1690d7802caeSToomas Soome 
1691d7802caeSToomas Soome 	/* We have regular file, register it and we are done. */
1692d7802caeSToomas Soome 	if (S_ISREG(sb.st_mode) != 0) {
1693d7802caeSToomas Soome 		pl->pl_devname = (char *)dev;
1694d7802caeSToomas Soome 
1695d7802caeSToomas Soome 		pl->pl_type = IB_BBLK_FILE;
1696d7802caeSToomas Soome 		pl->pl_cb.read = read_einfo_file_cb;
1697d7802caeSToomas Soome 		pl->pl_cb.print = print_einfo_cb;
1698d7802caeSToomas Soome 		STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1699d7802caeSToomas Soome 		return (true);
1700d7802caeSToomas Soome 	}
1701d7802caeSToomas Soome 
1702d7802caeSToomas Soome 	/*
1703d7802caeSToomas Soome 	 * This is block device.
1704d7802caeSToomas Soome 	 * We do not allow to specify whole disk device (cXtYdZp0 or cXtYdZ).
1705d7802caeSToomas Soome 	 */
1706d7802caeSToomas Soome 	if ((ptr = strrchr(dev, '/')) == NULL)
1707d7802caeSToomas Soome 		ptr = dev;
1708d7802caeSToomas Soome 	if ((strrchr(ptr, 'p') == NULL && strrchr(ptr, 's') == NULL) ||
1709d7802caeSToomas Soome 	    (dev[len - 2] == 'p' && dev[len - 1] == '0')) {
1710d7802caeSToomas Soome 		(void) fprintf(stderr,
1711d7802caeSToomas Soome 		    gettext("whole disk device is not supported\n"));
1712d7802caeSToomas Soome 		partlist_free(pl);
1713d7802caeSToomas Soome 		return (false);
1714d7802caeSToomas Soome 	}
1715d7802caeSToomas Soome 
1716d7802caeSToomas Soome 	data->target.path = (char *)dev;
1717d7802caeSToomas Soome 	if (!probe_fstyp(data)) {
1718d7802caeSToomas Soome 		partlist_free(pl);
1719d7802caeSToomas Soome 		return (false);
1720d7802caeSToomas Soome 	}
1721d7802caeSToomas Soome 
1722d7802caeSToomas Soome 	/* We start from identifying the whole disk. */
1723d7802caeSToomas Soome 	if ((p0 = strdup(dev)) == NULL) {
1724d7802caeSToomas Soome 		perror("calloc");
1725d7802caeSToomas Soome 		partlist_free(pl);
1726d7802caeSToomas Soome 		return (false);
1727d7802caeSToomas Soome 	}
1728d7802caeSToomas Soome 
1729d7802caeSToomas Soome 	pl->pl_devname = p0;
1730d7802caeSToomas Soome 	/* Change device name to p0 */
1731d7802caeSToomas Soome 	if ((ptr = strrchr(p0, 'p')) == NULL)
1732d7802caeSToomas Soome 		ptr = strrchr(p0, 's');
1733d7802caeSToomas Soome 	p0 = (char *)ptr;
1734d7802caeSToomas Soome 	p0[0] = 'p';
1735d7802caeSToomas Soome 	p0[1] = '0';
1736d7802caeSToomas Soome 	p0[2] = '\0';
1737d7802caeSToomas Soome 
1738d7802caeSToomas Soome 	if ((fd = open_device(pl->pl_devname)) == -1) {
1739d7802caeSToomas Soome 		partlist_free(pl);
1740d7802caeSToomas Soome 		return (false);
1741d7802caeSToomas Soome 	}
1742d7802caeSToomas Soome 
1743d7802caeSToomas Soome 	sector_size = get_media_info(fd);
1744d7802caeSToomas Soome 	(void) close(fd);
1745d7802caeSToomas Soome 
1746d7802caeSToomas Soome 	pl->pl_src_name = stage1;
1747d7802caeSToomas Soome 	pl->pl_type = IB_BBLK_MBR;
1748d7802caeSToomas Soome 	pl->pl_cb.compare = compare_mbr_cb;
1749d7802caeSToomas Soome 	pl->pl_cb.install = install_stage1_cb;
1750d7802caeSToomas Soome 	pl->pl_cb.read = read_stage1_cb;
1751d7802caeSToomas Soome 	pl->pl_cb.read_bbl = read_stage1_bbl_cb;
1752d7802caeSToomas Soome 	pl->pl_cb.print = print_stage1_cb;
1753d7802caeSToomas Soome 	STAILQ_INSERT_TAIL(data->plist, pl, pl_next);
1754d7802caeSToomas Soome 
1755d7802caeSToomas Soome 	if (probe_gpt(data))
1756d7802caeSToomas Soome 		return (true);
1757d7802caeSToomas Soome 
1758*0cc97882SToomas Soome 	/*
1759*0cc97882SToomas Soome 	 * We only do support sector size 512 with MBR and VTOC.
1760*0cc97882SToomas Soome 	 * The 4kn sector size with actual hardware is currently
1761*0cc97882SToomas Soome 	 * available with large disks and MBR/VTOC are not usable
1762*0cc97882SToomas Soome 	 * with such devices.
1763*0cc97882SToomas Soome 	 */
1764*0cc97882SToomas Soome 	if (sector_size != SECTOR_SIZE)
1765*0cc97882SToomas Soome 		return (false);
1766*0cc97882SToomas Soome 
1767d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1768d7802caeSToomas Soome 		if (probe_vtoc(data))
1769d7802caeSToomas Soome 			return (true);
1770d7802caeSToomas Soome 
1771d7802caeSToomas Soome 	if (data->device.devtype == IB_DEV_UNKNOWN)
1772d7802caeSToomas Soome 		return (probe_mbr(data));
1773d7802caeSToomas Soome 
1774d7802caeSToomas Soome 	return (false);
17750c946d80SToomas Soome }
17760c946d80SToomas Soome 
17770c946d80SToomas Soome static int
read_bootblock_from_file(const char * file,ib_bootblock_t * bblock)1778d7802caeSToomas Soome read_bootblock_from_file(const char *file, ib_bootblock_t *bblock)
17790c946d80SToomas Soome {
17800c946d80SToomas Soome 	struct stat	sb;
17810c946d80SToomas Soome 	uint32_t	buf_size;
17820c946d80SToomas Soome 	uint32_t	mboot_off;
17830c946d80SToomas Soome 	int		fd = -1;
17840c946d80SToomas Soome 	int		retval = BC_ERROR;
17850c946d80SToomas Soome 
1786b6dfa2aeSToomas Soome 	assert(bblock != NULL);
17870c946d80SToomas Soome 	assert(file != NULL);
17880c946d80SToomas Soome 
17890c946d80SToomas Soome 	fd = open(file, O_RDONLY);
17900c946d80SToomas Soome 	if (fd == -1) {
17910c946d80SToomas Soome 		BOOT_DEBUG("Error opening %s\n", file);
17920c946d80SToomas Soome 		goto out;
17930c946d80SToomas Soome 	}
17940c946d80SToomas Soome 
17950c946d80SToomas Soome 	if (fstat(fd, &sb) == -1) {
17960c946d80SToomas Soome 		BOOT_DEBUG("Error getting information (stat) about %s", file);
17970c946d80SToomas Soome 		perror("stat");
17980c946d80SToomas Soome 		goto outfd;
17990c946d80SToomas Soome 	}
18000c946d80SToomas Soome 
18010c946d80SToomas Soome 	/* loader bootblock has version built in */
18020c946d80SToomas Soome 	buf_size = sb.st_size;
180346d70dceSToomas Soome 	if (buf_size == 0)
180446d70dceSToomas Soome 		goto outfd;
18050c946d80SToomas Soome 
1806*0cc97882SToomas Soome 	/* Round up to sector size for raw disk write */
1807*0cc97882SToomas Soome 	bblock->buf_size = P2ROUNDUP(buf_size, sector_size);
18080c946d80SToomas Soome 	BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
18090c946d80SToomas Soome 	    bblock->buf_size);
18100c946d80SToomas Soome 
1811*0cc97882SToomas Soome 	bblock->buf = malloc(bblock->buf_size);
18120c946d80SToomas Soome 	if (bblock->buf == NULL) {
18130c946d80SToomas Soome 		perror(gettext("Memory allocation failure"));
18140c946d80SToomas Soome 		goto outbuf;
18150c946d80SToomas Soome 	}
18160c946d80SToomas Soome 	bblock->file = bblock->buf;
18170c946d80SToomas Soome 
1818*0cc97882SToomas Soome 	if (read(fd, bblock->file, buf_size) != buf_size) {
18190c946d80SToomas Soome 		BOOT_DEBUG("Read from %s failed\n", file);
18200c946d80SToomas Soome 		perror("read");
18210c946d80SToomas Soome 		goto outfd;
18220c946d80SToomas Soome 	}
18230c946d80SToomas Soome 
1824741b4d05SToomas Soome 	buf_size = MIN(buf_size, MBOOT_SCAN_SIZE);
1825741b4d05SToomas Soome 	if (find_multiboot(bblock->file, buf_size, &mboot_off)
18260c946d80SToomas Soome 	    != BC_SUCCESS) {
18270c946d80SToomas Soome 		(void) fprintf(stderr,
18280c946d80SToomas Soome 		    gettext("Unable to find multiboot header\n"));
18290c946d80SToomas Soome 		goto outfd;
18300c946d80SToomas Soome 	}
18310c946d80SToomas Soome 
18320c946d80SToomas Soome 	bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
18330c946d80SToomas Soome 	bblock->mboot_off = mboot_off;
18340c946d80SToomas Soome 
18350c946d80SToomas Soome 	bblock->file_size =
18360c946d80SToomas Soome 	    bblock->mboot->load_end_addr - bblock->mboot->load_addr;
18370c946d80SToomas Soome 	BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
18380c946d80SToomas Soome 
18390c946d80SToomas Soome 	bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
18400c946d80SToomas Soome 	bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
18410c946d80SToomas Soome 
18420c946d80SToomas Soome 	BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
18430c946d80SToomas Soome 	    "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
18440c946d80SToomas Soome 	    bblock->extra_size, bblock->buf, bblock->buf_size);
18450c946d80SToomas Soome 
18460c946d80SToomas Soome 	(void) close(fd);
18470c946d80SToomas Soome 	return (BC_SUCCESS);
18480c946d80SToomas Soome 
18490c946d80SToomas Soome outbuf:
18500c946d80SToomas Soome 	(void) free(bblock->buf);
18510c946d80SToomas Soome 	bblock->buf = NULL;
18520c946d80SToomas Soome outfd:
18530c946d80SToomas Soome 	(void) close(fd);
18540c946d80SToomas Soome out:
1855d7802caeSToomas Soome 	if (retval == BC_ERROR) {
1856d7802caeSToomas Soome 		(void) fprintf(stderr,
1857d7802caeSToomas Soome 		    gettext("Error reading bootblock from %s\n"),
1858d7802caeSToomas Soome 		    file);
1859d7802caeSToomas Soome 	}
1860d7802caeSToomas Soome 
1861d7802caeSToomas Soome 	if (retval == BC_NOEXTRA) {
1862d7802caeSToomas Soome 		BOOT_DEBUG("No multiboot header found on %s, unable to "
1863d7802caeSToomas Soome 		    "locate extra information area (old/non versioned "
1864d7802caeSToomas Soome 		    "bootblock?) \n", file);
1865d7802caeSToomas Soome 		(void) fprintf(stderr, gettext("No extended information"
1866d7802caeSToomas Soome 		    " found\n"));
1867d7802caeSToomas Soome 	}
18680c946d80SToomas Soome 	return (retval);
18690c946d80SToomas Soome }
18700c946d80SToomas Soome 
18710c946d80SToomas Soome static void
add_bootblock_einfo(ib_bootblock_t * bblock,char * updt_str)18720c946d80SToomas Soome add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
18730c946d80SToomas Soome {
18740c946d80SToomas Soome 	bblk_hs_t	hs;
18750c946d80SToomas Soome 	uint32_t	avail_space;
18760c946d80SToomas Soome 
18770c946d80SToomas Soome 	assert(bblock != NULL);
18780c946d80SToomas Soome 
18790c946d80SToomas Soome 	if (updt_str == NULL) {
18800c946d80SToomas Soome 		BOOT_DEBUG("WARNING: no update string passed to "
18810c946d80SToomas Soome 		    "add_bootblock_einfo()\n");
18820c946d80SToomas Soome 		return;
18830c946d80SToomas Soome 	}
18840c946d80SToomas Soome 
18850c946d80SToomas Soome 	/* Fill bootblock hashing source information. */
18860c946d80SToomas Soome 	hs.src_buf = (unsigned char *)bblock->file;
18870c946d80SToomas Soome 	hs.src_size = bblock->file_size;
18880c946d80SToomas Soome 	/* How much space for the extended information structure? */
18890c946d80SToomas Soome 	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
18900c946d80SToomas Soome 	/* Place the extended information structure. */
18910c946d80SToomas Soome 	add_einfo(bblock->extra, updt_str, &hs, avail_space);
18920c946d80SToomas Soome }
18930c946d80SToomas Soome 
18940c946d80SToomas Soome /*
18950c946d80SToomas Soome  * set up data for case stage1 is installed as MBR
18960c946d80SToomas Soome  * set up location and size of bootblock
18970c946d80SToomas Soome  * set disk guid to provide unique information for biosdev command
18980c946d80SToomas Soome  */
1899d7802caeSToomas Soome static void
prepare_stage1(struct partlist * stage1,struct partlist * stage2,uuid_t uuid)1900d7802caeSToomas Soome prepare_stage1(struct partlist *stage1, struct partlist *stage2, uuid_t uuid)
19010c946d80SToomas Soome {
1902d7802caeSToomas Soome 	char *src, *dest;
1903d7802caeSToomas Soome 	ib_bootblock_t *bblk;
19040c946d80SToomas Soome 	ib_device_t *device;
1905d7802caeSToomas Soome 	uint16_t size;
1906d7802caeSToomas Soome 	struct mboot *mbr;
19070c946d80SToomas Soome 
1908d7802caeSToomas Soome 	src = stage1->pl_stage;
1909d7802caeSToomas Soome 	dest = stage1->pl_src_data;
1910d7802caeSToomas Soome 	device = stage2->pl_device;
19110c946d80SToomas Soome 
1912d7802caeSToomas Soome 	/* Only copy from valid source. */
1913d7802caeSToomas Soome 	mbr = stage1->pl_stage;
1914d7802caeSToomas Soome 	if (mbr->signature == MBB_MAGIC) {
19150c946d80SToomas Soome 		/* copy BPB */
1916d7802caeSToomas Soome 		bcopy(src + STAGE1_BPB_OFFSET, dest + STAGE1_BPB_OFFSET,
1917d7802caeSToomas Soome 		    STAGE1_BPB_SIZE);
19180c946d80SToomas Soome 
19190c946d80SToomas Soome 		/* copy MBR, note STAGE1_SIG == BOOTSZ */
1920d7802caeSToomas Soome 		bcopy(src + STAGE1_SIG, dest + STAGE1_SIG,
19210c946d80SToomas Soome 		    SECTOR_SIZE - STAGE1_SIG);
1922d7802caeSToomas Soome 	}
1923d7802caeSToomas Soome 
1924d7802caeSToomas Soome 	bcopy(uuid, dest + STAGE1_STAGE2_UUID, UUID_LEN);
19250c946d80SToomas Soome 
1926*0cc97882SToomas Soome 	/* store bytes per sector */
1927*0cc97882SToomas Soome 	*((uint16_t *)(dest + STAGE1_BPB_BPS)) = sector_size;
19280c946d80SToomas Soome 	/* set stage2 size */
1929d7802caeSToomas Soome 	bblk = stage2->pl_src_data;
1930*0cc97882SToomas Soome 	size = bblk->buf_size / sector_size;
1931d7802caeSToomas Soome 	*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = size;
19320c946d80SToomas Soome 
1933d7802caeSToomas Soome 	/* set stage2 LBA */
1934d7802caeSToomas Soome 	*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
19350c946d80SToomas Soome 	    device->stage.start + device->stage.offset;
1936d7802caeSToomas Soome 
1937d7802caeSToomas Soome 	/* Copy prepared data to stage1 block read from the disk. */
1938d7802caeSToomas Soome 	bcopy(dest, src, SECTOR_SIZE);
19390c946d80SToomas Soome }
19400c946d80SToomas Soome 
1941d7802caeSToomas Soome static void
prepare_bootblock(ib_data_t * data,struct partlist * pl,char * updt_str)1942d7802caeSToomas Soome prepare_bootblock(ib_data_t *data, struct partlist *pl, char *updt_str)
19430c946d80SToomas Soome {
19440c946d80SToomas Soome 	ib_bootblock_t		*bblock;
19450c946d80SToomas Soome 	uint64_t		*ptr;
19460c946d80SToomas Soome 
1947d7802caeSToomas Soome 	assert(pl != NULL);
19480c946d80SToomas Soome 
1949d7802caeSToomas Soome 	bblock = pl->pl_src_data;
195046d70dceSToomas Soome 	if (bblock == NULL)
195146d70dceSToomas Soome 		return;
19520c946d80SToomas Soome 
19530c946d80SToomas Soome 	ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
1954d7802caeSToomas Soome 	*ptr = data->target.start;
19550c946d80SToomas Soome 
19560c946d80SToomas Soome 	/*
19570c946d80SToomas Soome 	 * the loader bootblock has built in version, if custom
19580c946d80SToomas Soome 	 * version was provided, update it.
19590c946d80SToomas Soome 	 */
19600c946d80SToomas Soome 	if (do_version)
19610c946d80SToomas Soome 		add_bootblock_einfo(bblock, updt_str);
19620c946d80SToomas Soome }
19630c946d80SToomas Soome 
19640c946d80SToomas Soome static int
open_device(const char * path)1965d7802caeSToomas Soome open_device(const char *path)
19660c946d80SToomas Soome {
19670c946d80SToomas Soome 	struct stat	statbuf = {0};
19680c946d80SToomas Soome 	int		fd = -1;
19690c946d80SToomas Soome 
19700c946d80SToomas Soome 	if (nowrite)
19710c946d80SToomas Soome 		fd = open(path, O_RDONLY);
19720c946d80SToomas Soome 	else
19730c946d80SToomas Soome 		fd = open(path, O_RDWR);
19740c946d80SToomas Soome 
19750c946d80SToomas Soome 	if (fd == -1) {
19760c946d80SToomas Soome 		BOOT_DEBUG("Unable to open %s\n", path);
19770c946d80SToomas Soome 		perror("open");
19780c946d80SToomas Soome 		return (-1);
19790c946d80SToomas Soome 	}
19800c946d80SToomas Soome 
19810c946d80SToomas Soome 	if (fstat(fd, &statbuf) != 0) {
19820c946d80SToomas Soome 		BOOT_DEBUG("Unable to stat %s\n", path);
19830c946d80SToomas Soome 		perror("stat");
19840c946d80SToomas Soome 		(void) close(fd);
19850c946d80SToomas Soome 		return (-1);
19860c946d80SToomas Soome 	}
19870c946d80SToomas Soome 
19880c946d80SToomas Soome 	if (S_ISCHR(statbuf.st_mode) == 0) {
19890c946d80SToomas Soome 		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
19900c946d80SToomas Soome 		    path);
19910c946d80SToomas Soome 		(void) close(fd);
19920c946d80SToomas Soome 		return (-1);
19930c946d80SToomas Soome 	}
19940c946d80SToomas Soome 
19950c946d80SToomas Soome 	return (fd);
19960c946d80SToomas Soome }
19970c946d80SToomas Soome 
1998d7802caeSToomas Soome /*
1999d7802caeSToomas Soome  * We need to record stage2 location and size into pmbr/vbr.
2000d7802caeSToomas Soome  * We need to record target partiton LBA to stage2.
2001d7802caeSToomas Soome  */
2002d7802caeSToomas Soome static void
prepare_bblocks(ib_data_t * data)2003d7802caeSToomas Soome prepare_bblocks(ib_data_t *data)
20040c946d80SToomas Soome {
2005d7802caeSToomas Soome 	struct partlist *pl;
2006d7802caeSToomas Soome 	struct partlist *mbr, *stage1, *stage2;
2007d7802caeSToomas Soome 	uuid_t uuid;
20080c946d80SToomas Soome 
20096b02bf31SToomas Soome 	/*
20106b02bf31SToomas Soome 	 * Create disk uuid. We only need reasonable amount of uniqueness
20116b02bf31SToomas Soome 	 * to allow biosdev to identify disk based on mbr differences.
20126b02bf31SToomas Soome 	 */
20136b02bf31SToomas Soome 	uuid_generate(uuid);
20146b02bf31SToomas Soome 
2015d7802caeSToomas Soome 	mbr = stage1 = stage2 = NULL;
20166b02bf31SToomas Soome 
20176b02bf31SToomas Soome 	/* First find stage 2. */
20186b02bf31SToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
20196b02bf31SToomas Soome 		if (pl->pl_type == IB_BBLK_STAGE2) {
20206b02bf31SToomas Soome 			stage2 = pl;
20216b02bf31SToomas Soome 
20226b02bf31SToomas Soome 			/*
20236b02bf31SToomas Soome 			 * When stage2 needs update, make sure we also
20246b02bf31SToomas Soome 			 * update stage1.
20256b02bf31SToomas Soome 			 */
20266b02bf31SToomas Soome 			if (pl->pl_cb.compare != NULL &&
20276b02bf31SToomas Soome 			    pl->pl_cb.compare(pl))
20286b02bf31SToomas Soome 				write_vbr = true;
20296b02bf31SToomas Soome 			break;
20306b02bf31SToomas Soome 		}
20316b02bf31SToomas Soome 	}
2032d7802caeSToomas Soome 	/*
2033d7802caeSToomas Soome 	 * Walk list and pick up BIOS boot blocks. EFI boot programs
2034d7802caeSToomas Soome 	 * can be set in place.
2035d7802caeSToomas Soome 	 */
2036d7802caeSToomas Soome 	STAILQ_FOREACH(pl, data->plist, pl_next) {
2037d7802caeSToomas Soome 		switch (pl->pl_type) {
2038d7802caeSToomas Soome 		case IB_BBLK_MBR:
2039d7802caeSToomas Soome 			mbr = pl;
2040d7802caeSToomas Soome 			break;
2041d7802caeSToomas Soome 		case IB_BBLK_STAGE1:
2042d7802caeSToomas Soome 			stage1 = pl;
20436b02bf31SToomas Soome 			if (stage2 != NULL)
20446b02bf31SToomas Soome 				prepare_stage1(stage1, stage2, uuid);
2045d7802caeSToomas Soome 			break;
2046d7802caeSToomas Soome 		case IB_BBLK_STAGE2:
2047d7802caeSToomas Soome 		case IB_BBLK_EFI:
2048d7802caeSToomas Soome 			prepare_bootblock(data, pl, update_str);
2049d7802caeSToomas Soome 			break;
2050d7802caeSToomas Soome 		default:
20510c946d80SToomas Soome 			break;
20520c946d80SToomas Soome 		}
20530c946d80SToomas Soome 	}
20540c946d80SToomas Soome 
2055d7802caeSToomas Soome 	/* If stage2 is missing, we are done. */
2056d7802caeSToomas Soome 	if (stage2 == NULL)
2057d7802caeSToomas Soome 		return;
20580c946d80SToomas Soome 
2059d7802caeSToomas Soome 	if (mbr != NULL) {
2060d7802caeSToomas Soome 		prepare_stage1(mbr, stage2, uuid);
20610c946d80SToomas Soome 
2062d7802caeSToomas Soome 		/*
2063d7802caeSToomas Soome 		 * If we have stage1, we point MBR to read stage 1.
2064d7802caeSToomas Soome 		 */
2065d7802caeSToomas Soome 		if (stage1 != NULL) {
2066d7802caeSToomas Soome 			char *dest = mbr->pl_stage;
2067d7802caeSToomas Soome 
2068d7802caeSToomas Soome 			*((uint16_t *)(dest + STAGE1_STAGE2_SIZE)) = 1;
2069d7802caeSToomas Soome 			*((uint64_t *)(dest + STAGE1_STAGE2_LBA)) =
2070d7802caeSToomas Soome 			    stage1->pl_device->stage.start;
2071d7802caeSToomas Soome 		}
20720c946d80SToomas Soome 	}
20730c946d80SToomas Soome }
20740c946d80SToomas Soome 
20750c946d80SToomas Soome /*
20760c946d80SToomas Soome  * Install a new bootblock on the given device. handle_install() expects argv
20770c946d80SToomas Soome  * to contain 3 parameters (the target device path and the path to the
20780c946d80SToomas Soome  * bootblock.
20790c946d80SToomas Soome  *
20800c946d80SToomas Soome  * Returns:	BC_SUCCESS - if the installation is successful
20810c946d80SToomas Soome  *		BC_ERROR   - if the installation failed
20820c946d80SToomas Soome  *		BC_NOUPDT  - if no installation was performed because the
20830c946d80SToomas Soome  *		             version currently installed is more recent than the
20840c946d80SToomas Soome  *			     supplied one.
20850c946d80SToomas Soome  *
20860c946d80SToomas Soome  */
20870c946d80SToomas Soome static int
handle_install(char * progname,int argc,char ** argv)2088d7802caeSToomas Soome handle_install(char *progname, int argc, char **argv)
20890c946d80SToomas Soome {
2090d7802caeSToomas Soome 	struct partlist	*pl;
2091d7802caeSToomas Soome 	ib_data_t	data = { 0 };
20920c946d80SToomas Soome 	char		*device_path = NULL;
20930c946d80SToomas Soome 	int		ret = BC_ERROR;
20940c946d80SToomas Soome 
2095d7802caeSToomas Soome 	switch (argc) {
2096d7802caeSToomas Soome 	case 1:
2097d7802caeSToomas Soome 		if ((device_path = strdup(argv[0])) == NULL) {
2098d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2099d7802caeSToomas Soome 			goto done;
21000c946d80SToomas Soome 		}
2101d7802caeSToomas Soome 		if (asprintf(&stage1, "%s/%s", boot_dir, STAGE1) < 0) {
2102d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2103d7802caeSToomas Soome 			goto done;
2104d7802caeSToomas Soome 		}
2105d7802caeSToomas Soome 		if (asprintf(&stage2, "%s/%s", boot_dir, STAGE2) < 0) {
2106d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2107d7802caeSToomas Soome 			goto done;
2108d7802caeSToomas Soome 		}
2109d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2110d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2111d7802caeSToomas Soome 			goto done;
2112d7802caeSToomas Soome 		}
2113d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2114d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2115d7802caeSToomas Soome 			goto done;
2116d7802caeSToomas Soome 		}
2117d7802caeSToomas Soome 		break;
2118d7802caeSToomas Soome 	case 3:
2119d7802caeSToomas Soome 		if ((stage1 = strdup(argv[0])) == NULL) {
2120d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2121d7802caeSToomas Soome 			goto done;
2122d7802caeSToomas Soome 		}
2123d7802caeSToomas Soome 		if ((stage2 = strdup(argv[1])) == NULL) {
2124d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2125d7802caeSToomas Soome 			goto done;
2126d7802caeSToomas Soome 		}
2127d7802caeSToomas Soome 		if ((device_path = strdup(argv[2])) == NULL) {
2128d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2129d7802caeSToomas Soome 			goto done;
2130d7802caeSToomas Soome 		}
2131d7802caeSToomas Soome 		if (asprintf(&efi32, "%s/%s", boot_dir, LOADER32) < 0) {
2132d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2133d7802caeSToomas Soome 			goto done;
2134d7802caeSToomas Soome 		}
2135d7802caeSToomas Soome 		if (asprintf(&efi64, "%s/%s", boot_dir, LOADER64) < 0) {
2136d7802caeSToomas Soome 			perror(gettext("Memory Allocation Failure"));
2137d7802caeSToomas Soome 			goto done;
2138d7802caeSToomas Soome 		}
2139d7802caeSToomas Soome 		break;
2140d7802caeSToomas Soome 	default:
2141d7802caeSToomas Soome 		usage(progname, ret);
2142d7802caeSToomas Soome 	}
2143d7802caeSToomas Soome 
2144d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2145d7802caeSToomas Soome 	if (data.plist == NULL) {
2146d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2147d7802caeSToomas Soome 		goto done;
2148d7802caeSToomas Soome 	}
2149d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
21500c946d80SToomas Soome 
21510c946d80SToomas Soome 	BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
2152d7802caeSToomas Soome 	    device_path, stage1, stage2);
21530c946d80SToomas Soome 
2154d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2155d7802caeSToomas Soome 		/* Read all data. */
2156d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2157d7802caeSToomas Soome 			if (!pl->pl_cb.read(pl)) {
2158d7802caeSToomas Soome 				printf("\n");
2159d7802caeSToomas Soome 			}
2160d7802caeSToomas Soome 			if (!pl->pl_cb.read_bbl(pl)) {
216146d70dceSToomas Soome 				/*
216246d70dceSToomas Soome 				 * We will ignore ESP updates in case of
216346d70dceSToomas Soome 				 * older system where we are missing
216446d70dceSToomas Soome 				 * loader64.efi and loader32.efi.
216546d70dceSToomas Soome 				 */
216646d70dceSToomas Soome 				if (pl->pl_type != IB_BBLK_EFI)
2167d7802caeSToomas Soome 					goto cleanup;
2168d7802caeSToomas Soome 			}
21690c946d80SToomas Soome 		}
21700c946d80SToomas Soome 
2171d7802caeSToomas Soome 		/* Prepare data. */
2172d7802caeSToomas Soome 		prepare_bblocks(&data);
2173d7802caeSToomas Soome 
2174d7802caeSToomas Soome 		/* Commit data to disk. */
2175d7802caeSToomas Soome 		while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) !=
2176d7802caeSToomas Soome 		    NULL) {
2177d7802caeSToomas Soome 			if (pl->pl_cb.compare != NULL &&
2178d7802caeSToomas Soome 			    pl->pl_cb.compare(pl)) {
2179d7802caeSToomas Soome 				if (pl->pl_cb.install != NULL)
2180d7802caeSToomas Soome 					pl->pl_cb.install(&data, pl);
21810c946d80SToomas Soome 			}
2182d7802caeSToomas Soome 			STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2183d7802caeSToomas Soome 			partlist_free(pl);
21840c946d80SToomas Soome 		}
21850c946d80SToomas Soome 	}
2186d7802caeSToomas Soome 	ret = BC_SUCCESS;
21870c946d80SToomas Soome 
2188d7802caeSToomas Soome cleanup:
2189d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2190d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2191d7802caeSToomas Soome 		partlist_free(pl);
2192d7802caeSToomas Soome 	}
2193d7802caeSToomas Soome 	free(data.plist);
2194d7802caeSToomas Soome done:
21950c946d80SToomas Soome 	free(stage1);
2196d7802caeSToomas Soome 	free(stage2);
2197d7802caeSToomas Soome 	free(efi32);
2198d7802caeSToomas Soome 	free(efi64);
21990c946d80SToomas Soome 	free(device_path);
22000c946d80SToomas Soome 	return (ret);
22010c946d80SToomas Soome }
22020c946d80SToomas Soome 
22030c946d80SToomas Soome /*
22040c946d80SToomas Soome  * Retrieves from a device the extended information (einfo) associated to the
2205b6dfa2aeSToomas Soome  * file or installed stage2.
2206b6dfa2aeSToomas Soome  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
2207b6dfa2aeSToomas Soome  * or file name.
22080c946d80SToomas Soome  * Returns:
22090c946d80SToomas Soome  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
22100c946d80SToomas Soome  *	  - BC_ERROR (on error)
22110c946d80SToomas Soome  *        - BC_NOEINFO (no extended information available)
22120c946d80SToomas Soome  */
22130c946d80SToomas Soome static int
handle_getinfo(char * progname,int argc,char ** argv)2214d7802caeSToomas Soome handle_getinfo(char *progname, int argc, char **argv)
22150c946d80SToomas Soome {
2216d7802caeSToomas Soome 	struct partlist	*pl;
2217d7802caeSToomas Soome 	ib_data_t	data = { 0 };
2218d7802caeSToomas Soome 	char		*device_path;
22190c946d80SToomas Soome 
2220d7802caeSToomas Soome 	if (argc != 1) {
22210c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2222bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22230c946d80SToomas Soome 	}
22240c946d80SToomas Soome 
2225d7802caeSToomas Soome 	if ((device_path = strdup(argv[0])) == NULL) {
2226d7802caeSToomas Soome 		perror(gettext("Memory Allocation Failure"));
2227d7802caeSToomas Soome 		return (BC_ERROR);
22280c946d80SToomas Soome 	}
22290c946d80SToomas Soome 
2230d7802caeSToomas Soome 	data.plist = malloc(sizeof (*data.plist));
2231d7802caeSToomas Soome 	if (data.plist == NULL) {
2232d7802caeSToomas Soome 		perror("malloc");
22330c946d80SToomas Soome 		free(device_path);
2234d7802caeSToomas Soome 		return (BC_ERROR);
2235d7802caeSToomas Soome 	}
2236d7802caeSToomas Soome 	STAILQ_INIT(data.plist);
2237d7802caeSToomas Soome 
2238d7802caeSToomas Soome 	if (probe_device(&data, device_path)) {
2239d7802caeSToomas Soome 		STAILQ_FOREACH(pl, data.plist, pl_next) {
2240d7802caeSToomas Soome 			if (pl->pl_cb.read(pl))
2241d7802caeSToomas Soome 				pl->pl_cb.print(pl);
2242d7802caeSToomas Soome 			else
2243d7802caeSToomas Soome 				printf("\n");
2244d7802caeSToomas Soome 		}
2245d7802caeSToomas Soome 	}
2246d7802caeSToomas Soome 
2247d7802caeSToomas Soome 	while ((pl = STAILQ_LAST(data.plist, partlist, pl_next)) != NULL) {
2248d7802caeSToomas Soome 		STAILQ_REMOVE(data.plist, pl, partlist, pl_next);
2249d7802caeSToomas Soome 		partlist_free(pl);
2250d7802caeSToomas Soome 	}
2251d7802caeSToomas Soome 	free(data.plist);
2252d7802caeSToomas Soome 
2253d7802caeSToomas Soome 	return (BC_SUCCESS);
22540c946d80SToomas Soome }
22550c946d80SToomas Soome 
22560c946d80SToomas Soome /*
22570c946d80SToomas Soome  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
22580c946d80SToomas Soome  *
22590c946d80SToomas Soome  * Returns:
22600c946d80SToomas Soome  *	- BC_SUCCESS (a successful propagation happened)
22610c946d80SToomas Soome  *	- BC_ERROR (an error occurred)
22620c946d80SToomas Soome  *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
22630c946d80SToomas Soome  *			there is no multiboot information)
22640c946d80SToomas Soome  */
22650c946d80SToomas Soome static int
handle_mirror(char * progname,int argc,char ** argv)2266d7802caeSToomas Soome handle_mirror(char *progname, int argc, char **argv)
22670c946d80SToomas Soome {
2268d7802caeSToomas Soome 	ib_data_t src = { 0 };
2269d7802caeSToomas Soome 	ib_data_t dest = { 0 };
2270d7802caeSToomas Soome 	struct partlist *pl_src, *pl_dest;
2271d7802caeSToomas Soome 	char		*curr_device_path = NULL;
2272d7802caeSToomas Soome 	char		*attach_device_path = NULL;
22730c946d80SToomas Soome 	int		retval = BC_ERROR;
22740c946d80SToomas Soome 
2275d7802caeSToomas Soome 	if (argc == 2) {
22760c946d80SToomas Soome 		curr_device_path = strdup(argv[0]);
22770c946d80SToomas Soome 		attach_device_path = strdup(argv[1]);
2278d7802caeSToomas Soome 	}
22790c946d80SToomas Soome 
22800c946d80SToomas Soome 	if (!curr_device_path || !attach_device_path) {
2281d7802caeSToomas Soome 		free(curr_device_path);
2282d7802caeSToomas Soome 		free(attach_device_path);
22830c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Missing parameter"));
2284bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
22850c946d80SToomas Soome 	}
22860c946d80SToomas Soome 	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
22870c946d80SToomas Soome 	    " %s\n", curr_device_path, attach_device_path);
22880c946d80SToomas Soome 
2289d7802caeSToomas Soome 	src.plist = malloc(sizeof (*src.plist));
2290d7802caeSToomas Soome 	if (src.plist == NULL) {
2291d7802caeSToomas Soome 		perror("malloc");
2292d7802caeSToomas Soome 		return (BC_ERROR);
2293d7802caeSToomas Soome 	}
2294d7802caeSToomas Soome 	STAILQ_INIT(src.plist);
22950c946d80SToomas Soome 
2296d7802caeSToomas Soome 	dest.plist = malloc(sizeof (*dest.plist));
2297d7802caeSToomas Soome 	if (dest.plist == NULL) {
2298d7802caeSToomas Soome 		perror("malloc");
2299d7802caeSToomas Soome 		goto out;
2300d7802caeSToomas Soome 	}
2301d7802caeSToomas Soome 	STAILQ_INIT(dest.plist);
2302d7802caeSToomas Soome 
2303d7802caeSToomas Soome 	if (!probe_device(&src, curr_device_path)) {
23040c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
23050c946d80SToomas Soome 		    "information from %s (current device)\n"),
23060c946d80SToomas Soome 		    curr_device_path);
2307d7802caeSToomas Soome 		goto out;
23080c946d80SToomas Soome 	}
23090c946d80SToomas Soome 
2310d7802caeSToomas Soome 	if (!probe_device(&dest, attach_device_path) != BC_SUCCESS) {
23110c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Unable to gather device "
23120c946d80SToomas Soome 		    "information from %s (attaching device)\n"),
23130c946d80SToomas Soome 		    attach_device_path);
2314d7802caeSToomas Soome 		goto cleanup_src;
23150c946d80SToomas Soome 	}
23160c946d80SToomas Soome 
23179937ff19SToomas Soome 	write_vbr = true;
2318d7802caeSToomas Soome 	write_mbr = true;
2319d7802caeSToomas Soome 	force_mbr = true;
2320d7802caeSToomas Soome 
2321d7802caeSToomas Soome 	pl_dest = STAILQ_FIRST(dest.plist);
2322d7802caeSToomas Soome 	STAILQ_FOREACH(pl_src, src.plist, pl_next) {
2323d7802caeSToomas Soome 		if (pl_dest == NULL) {
2324d7802caeSToomas Soome 			(void) fprintf(stderr,
2325d7802caeSToomas Soome 			    gettext("Destination disk layout is different "
2326d7802caeSToomas Soome 			    "from source, can not mirror.\n"));
2327d7802caeSToomas Soome 			goto cleanup;
2328d7802caeSToomas Soome 		}
2329d7802caeSToomas Soome 		if (!pl_src->pl_cb.read(pl_src)) {
2330d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2331d7802caeSToomas Soome 			    "boot block from %s\n"), pl_src->pl_devname);
2332d7802caeSToomas Soome 			goto cleanup;
2333d7802caeSToomas Soome 		}
2334d7802caeSToomas Soome 		if (!pl_dest->pl_cb.read(pl_dest)) {
2335d7802caeSToomas Soome 			(void) fprintf(stderr, gettext("Failed to read "
2336d7802caeSToomas Soome 			    "boot block from %s\n"), pl_dest->pl_devname);
23370c946d80SToomas Soome 		}
23380c946d80SToomas Soome 
2339d7802caeSToomas Soome 		/* Set source pl_stage to destination source data */
2340d7802caeSToomas Soome 		pl_dest->pl_src_data = pl_src->pl_stage;
2341d7802caeSToomas Soome 		pl_src->pl_stage = NULL;
2342d7802caeSToomas Soome 
2343d7802caeSToomas Soome 		pl_dest = STAILQ_NEXT(pl_dest, pl_next);
23440c946d80SToomas Soome 	}
23450c946d80SToomas Soome 
2346d7802caeSToomas Soome 	/* Prepare data. */
2347d7802caeSToomas Soome 	prepare_bblocks(&dest);
23480c946d80SToomas Soome 
2349d7802caeSToomas Soome 	/* Commit data to disk. */
2350d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2351d7802caeSToomas Soome 		pl_dest->pl_cb.install(&dest, pl_dest);
2352d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2353d7802caeSToomas Soome 		partlist_free(pl_dest);
2354d7802caeSToomas Soome 
2355d7802caeSToomas Soome 		/* Free source list */
2356d7802caeSToomas Soome 		pl_src = STAILQ_LAST(src.plist, partlist, pl_next);
2357d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2358d7802caeSToomas Soome 		partlist_free(pl_src);
2359d7802caeSToomas Soome 	}
2360d7802caeSToomas Soome 	retval = BC_SUCCESS;
2361d7802caeSToomas Soome 
2362d7802caeSToomas Soome cleanup:
2363d7802caeSToomas Soome 	while ((pl_dest = STAILQ_LAST(dest.plist, partlist, pl_next)) != NULL) {
2364d7802caeSToomas Soome 		STAILQ_REMOVE(dest.plist, pl_dest, partlist, pl_next);
2365d7802caeSToomas Soome 		partlist_free(pl_dest);
2366d7802caeSToomas Soome 	}
2367d7802caeSToomas Soome 	free(dest.plist);
2368d7802caeSToomas Soome cleanup_src:
2369d7802caeSToomas Soome 	while ((pl_src = STAILQ_LAST(src.plist, partlist, pl_next)) != NULL) {
2370d7802caeSToomas Soome 		STAILQ_REMOVE(src.plist, pl_src, partlist, pl_next);
2371d7802caeSToomas Soome 		partlist_free(pl_src);
2372d7802caeSToomas Soome 	}
2373d7802caeSToomas Soome 	free(src.plist);
2374d7802caeSToomas Soome out:
23750c946d80SToomas Soome 	free(curr_device_path);
23760c946d80SToomas Soome 	free(attach_device_path);
23770c946d80SToomas Soome 	return (retval);
23780c946d80SToomas Soome }
23790c946d80SToomas Soome 
2380d7802caeSToomas Soome #define	USAGE_STRING	\
2381d7802caeSToomas Soome "Usage:\t%s [-fFmn] [-b boot_dir] [-u verstr]\n"	\
2382d7802caeSToomas Soome "\t\t[stage1 stage2] raw-device\n"			\
23830c946d80SToomas Soome "\t%s -M [-n] raw-device attach-raw-device\n"		\
2384b6dfa2aeSToomas Soome "\t%s [-e|-V] -i raw-device | file\n"
23850c946d80SToomas Soome 
23860c946d80SToomas Soome #define	CANON_USAGE_STR	gettext(USAGE_STRING)
23870c946d80SToomas Soome 
23880c946d80SToomas Soome static void
usage(char * progname,int rc)2389bdecfb1eSToomas Soome usage(char *progname, int rc)
23900c946d80SToomas Soome {
23910c946d80SToomas Soome 	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
2392d7802caeSToomas Soome 	fini_yes();
2393bdecfb1eSToomas Soome 	exit(rc);
23940c946d80SToomas Soome }
23950c946d80SToomas Soome 
23960c946d80SToomas Soome int
main(int argc,char ** argv)23970c946d80SToomas Soome main(int argc, char **argv)
23980c946d80SToomas Soome {
23990c946d80SToomas Soome 	int	opt;
24000c946d80SToomas Soome 	int	ret;
24010c946d80SToomas Soome 	char	*progname;
2402d7802caeSToomas Soome 	struct stat sb;
24030c946d80SToomas Soome 
24040c946d80SToomas Soome 	(void) setlocale(LC_ALL, "");
24050c946d80SToomas Soome 	(void) textdomain(TEXT_DOMAIN);
2406bdecfb1eSToomas Soome 	if (init_yes() < 0)
2407bdecfb1eSToomas Soome 		errx(BC_ERROR, gettext(ERR_MSG_INIT_YES), strerror(errno));
2408bdecfb1eSToomas Soome 
2409d7802caeSToomas Soome 	/* Needed for mount pcfs. */
2410d7802caeSToomas Soome 	tzset();
2411d7802caeSToomas Soome 
2412bdecfb1eSToomas Soome 	/* Determine our name */
2413bdecfb1eSToomas Soome 	progname = basename(argv[0]);
24140c946d80SToomas Soome 
2415d7802caeSToomas Soome 	while ((opt = getopt(argc, argv, "b:deFfhiMmnu:V")) != EOF) {
24160c946d80SToomas Soome 		switch (opt) {
2417d7802caeSToomas Soome 		case 'b':
2418d7802caeSToomas Soome 			boot_dir = strdup(optarg);
2419d7802caeSToomas Soome 			if (boot_dir == NULL) {
2420d7802caeSToomas Soome 				err(BC_ERROR,
2421d7802caeSToomas Soome 				    gettext("Memory allocation failure"));
2422d7802caeSToomas Soome 			}
2423d7802caeSToomas Soome 			if (lstat(boot_dir, &sb) != 0) {
2424d7802caeSToomas Soome 				err(BC_ERROR, boot_dir);
2425d7802caeSToomas Soome 			}
2426d7802caeSToomas Soome 			if (!S_ISDIR(sb.st_mode)) {
2427d7802caeSToomas Soome 				errx(BC_ERROR, gettext("%s: not a directory"),
2428d7802caeSToomas Soome 				    boot_dir);
2429d7802caeSToomas Soome 			}
2430d7802caeSToomas Soome 			break;
24310c946d80SToomas Soome 		case 'd':
2432d7802caeSToomas Soome 			boot_debug = true;
24330c946d80SToomas Soome 			break;
24340c946d80SToomas Soome 		case 'e':
2435d7802caeSToomas Soome 			strip = true;
24360c946d80SToomas Soome 			break;
24370c946d80SToomas Soome 		case 'F':
2438d7802caeSToomas Soome 			force_update = true;
24390c946d80SToomas Soome 			break;
24400c946d80SToomas Soome 		case 'f':
2441d7802caeSToomas Soome 			force_mbr = true;
24420c946d80SToomas Soome 			break;
24430c946d80SToomas Soome 		case 'h':
2444bdecfb1eSToomas Soome 			usage(progname, BC_SUCCESS);
24450c946d80SToomas Soome 			break;
24460c946d80SToomas Soome 		case 'i':
2447d7802caeSToomas Soome 			do_getinfo = true;
24480c946d80SToomas Soome 			break;
24490c946d80SToomas Soome 		case 'M':
2450d7802caeSToomas Soome 			do_mirror_bblk = true;
24510c946d80SToomas Soome 			break;
24520c946d80SToomas Soome 		case 'm':
2453d7802caeSToomas Soome 			write_mbr = true;
24540c946d80SToomas Soome 			break;
24550c946d80SToomas Soome 		case 'n':
2456d7802caeSToomas Soome 			nowrite = true;
24570c946d80SToomas Soome 			break;
24580c946d80SToomas Soome 		case 'u':
2459d7802caeSToomas Soome 			do_version = true;
24600c946d80SToomas Soome 
2461bdecfb1eSToomas Soome 			update_str = strdup(optarg);
24620c946d80SToomas Soome 			if (update_str == NULL) {
24630c946d80SToomas Soome 				perror(gettext("Memory allocation failure"));
24640c946d80SToomas Soome 				exit(BC_ERROR);
24650c946d80SToomas Soome 			}
24660c946d80SToomas Soome 			break;
24670c946d80SToomas Soome 		case 'V':
2468d7802caeSToomas Soome 			verbose_dump = true;
24690c946d80SToomas Soome 			break;
24700c946d80SToomas Soome 		default:
24710c946d80SToomas Soome 			/* fall through to process non-optional args */
24720c946d80SToomas Soome 			break;
24730c946d80SToomas Soome 		}
24740c946d80SToomas Soome 	}
24750c946d80SToomas Soome 
24760c946d80SToomas Soome 	/* check arguments */
24770c946d80SToomas Soome 	check_options(progname);
24780c946d80SToomas Soome 
24790c946d80SToomas Soome 	if (nowrite)
24800c946d80SToomas Soome 		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
24810c946d80SToomas Soome 		    " be written to disk.\n"));
24820c946d80SToomas Soome 
24830c946d80SToomas Soome 	if (do_getinfo) {
2484d7802caeSToomas Soome 		ret = handle_getinfo(progname, argc - optind, argv + optind);
24850c946d80SToomas Soome 	} else if (do_mirror_bblk) {
2486d7802caeSToomas Soome 		ret = handle_mirror(progname, argc - optind, argv + optind);
24870c946d80SToomas Soome 	} else {
2488d7802caeSToomas Soome 		ret = handle_install(progname, argc - optind, argv + optind);
24890c946d80SToomas Soome 	}
2490d7802caeSToomas Soome 	fini_yes();
24910c946d80SToomas Soome 	return (ret);
24920c946d80SToomas Soome }
24930c946d80SToomas Soome 
24940c946d80SToomas Soome #define	MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
24950c946d80SToomas Soome static void
check_options(char * progname)24960c946d80SToomas Soome check_options(char *progname)
24970c946d80SToomas Soome {
24980c946d80SToomas Soome 	if (do_getinfo && do_mirror_bblk) {
24990c946d80SToomas Soome 		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
25000c946d80SToomas Soome 		    "specified at the same time\n"));
2501bdecfb1eSToomas Soome 		usage(progname, BC_ERROR);
25020c946d80SToomas Soome 	}
25030c946d80SToomas Soome 
25040c946d80SToomas Soome 	if (do_mirror_bblk) {
25050c946d80SToomas Soome 		/*
25060c946d80SToomas Soome 		 * -u and -F may actually reflect a user intent that is not
25070c946d80SToomas Soome 		 * correct with this command (mirror can be interpreted
25080c946d80SToomas Soome 		 * "similar" to install. Emit a message and continue.
25090c946d80SToomas Soome 		 * -e and -V have no meaning, be quiet here and only report the
25100c946d80SToomas Soome 		 * incongruence if a debug output is requested.
25110c946d80SToomas Soome 		 */
25120c946d80SToomas Soome 		if (do_version) {
25130c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-u");
2514d7802caeSToomas Soome 			do_version = false;
25150c946d80SToomas Soome 		}
25160c946d80SToomas Soome 		if (force_update) {
25170c946d80SToomas Soome 			(void) fprintf(stderr, MEANINGLESS_OPT, "-F");
2518d7802caeSToomas Soome 			force_update = false;
25190c946d80SToomas Soome 		}
25200c946d80SToomas Soome 		if (strip || verbose_dump) {
25210c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
2522d7802caeSToomas Soome 			strip = false;
2523d7802caeSToomas Soome 			verbose_dump = false;
25240c946d80SToomas Soome 		}
25250c946d80SToomas Soome 	}
25260c946d80SToomas Soome 
2527d7802caeSToomas Soome 	if ((strip || verbose_dump) && !do_getinfo)
2528d7802caeSToomas Soome 		usage(progname, BC_ERROR);
2529d7802caeSToomas Soome 
25300c946d80SToomas Soome 	if (do_getinfo) {
25310c946d80SToomas Soome 		if (write_mbr || force_mbr || do_version || force_update) {
25320c946d80SToomas Soome 			BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
2533d7802caeSToomas Soome 			write_mbr = force_mbr = do_version = false;
2534d7802caeSToomas Soome 			force_update = false;
25350c946d80SToomas Soome 		}
25360c946d80SToomas Soome 	}
25370c946d80SToomas Soome }
2538