xref: /openbsd-src/usr.sbin/installboot/i386_softraid.c (revision 210008b3130591ff79d2d5024ffeaba6feea47ff)
1*210008b3Sjsing /*	$OpenBSD: i386_softraid.c,v 1.2 2014/06/09 13:13:48 jsing Exp $	*/
2b4544c7cSjsing /*
3b4544c7cSjsing  * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
4b4544c7cSjsing  *
5b4544c7cSjsing  * Permission to use, copy, modify, and distribute this software for any
6b4544c7cSjsing  * purpose with or without fee is hereby granted, provided that the above
7b4544c7cSjsing  * copyright notice and this permission notice appear in all copies.
8b4544c7cSjsing  *
9b4544c7cSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b4544c7cSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b4544c7cSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b4544c7cSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b4544c7cSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b4544c7cSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b4544c7cSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b4544c7cSjsing  */
17b4544c7cSjsing 
18b4544c7cSjsing #include <sys/param.h>
19b4544c7cSjsing #include <sys/disklabel.h>
20b4544c7cSjsing #include <sys/dkio.h>
21b4544c7cSjsing #include <sys/ioctl.h>
22b4544c7cSjsing #include <sys/stat.h>
23b4544c7cSjsing 
24b4544c7cSjsing #include <dev/biovar.h>
25b4544c7cSjsing #include <dev/softraidvar.h>
26b4544c7cSjsing #include <ufs/ufs/dinode.h>
27b4544c7cSjsing 
28b4544c7cSjsing #include <err.h>
29b4544c7cSjsing #include <fcntl.h>
30b4544c7cSjsing #include <stdio.h>
31b4544c7cSjsing #include <stdlib.h>
32b4544c7cSjsing #include <string.h>
33b4544c7cSjsing #include <unistd.h>
34b4544c7cSjsing #include <util.h>
35b4544c7cSjsing 
36b4544c7cSjsing #include "installboot.h"
37b4544c7cSjsing #include "i386_installboot.h"
38b4544c7cSjsing 
39b4544c7cSjsing void	sr_install_bootblk(int, int, int);
40b4544c7cSjsing void	sr_install_bootldr(int, char *);
41b4544c7cSjsing 
42b4544c7cSjsing void
43b4544c7cSjsing sr_install_bootblk(int devfd, int vol, int disk)
44b4544c7cSjsing {
45b4544c7cSjsing 	struct bioc_disk bd;
46b4544c7cSjsing 	struct disklabel dl;
47b4544c7cSjsing 	struct partition *pp;
48b4544c7cSjsing 	uint32_t poffset;
49b4544c7cSjsing 	char *dev;
50b4544c7cSjsing 	char part;
51b4544c7cSjsing 	int diskfd;
52b4544c7cSjsing 
53b4544c7cSjsing 	/* Get device name for this disk/chunk. */
54b4544c7cSjsing 	memset(&bd, 0, sizeof(bd));
55b4544c7cSjsing 	bd.bd_volid = vol;
56b4544c7cSjsing 	bd.bd_diskid = disk;
57b4544c7cSjsing 	if (ioctl(devfd, BIOCDISK, &bd) == -1)
58b4544c7cSjsing 		err(1, "BIOCDISK");
59b4544c7cSjsing 
60b4544c7cSjsing 	/* Check disk status. */
61b4544c7cSjsing 	if (bd.bd_status != BIOC_SDONLINE && bd.bd_status != BIOC_SDREBUILD) {
62b4544c7cSjsing 		fprintf(stderr, "softraid chunk %u not online - skipping...\n",
63b4544c7cSjsing 		    disk);
64b4544c7cSjsing 		return;
65b4544c7cSjsing 	}
66b4544c7cSjsing 
67b4544c7cSjsing 	if (strlen(bd.bd_vendor) < 1)
68b4544c7cSjsing 		errx(1, "invalid disk name");
69b4544c7cSjsing 	part = bd.bd_vendor[strlen(bd.bd_vendor) - 1];
70b4544c7cSjsing 	if (part < 'a' || part >= 'a' + MAXPARTITIONS)
71b4544c7cSjsing 		errx(1, "invalid partition %c\n", part);
72b4544c7cSjsing 	bd.bd_vendor[strlen(bd.bd_vendor) - 1] = '\0';
73b4544c7cSjsing 
74b4544c7cSjsing 	/* Open this device and check its disklabel. */
75b4544c7cSjsing 	if ((diskfd = opendev(bd.bd_vendor, (nowrite? O_RDONLY:O_RDWR),
76b4544c7cSjsing 	    OPENDEV_PART, &dev)) < 0)
77b4544c7cSjsing 		err(1, "open: %s", dev);
78b4544c7cSjsing 
79b4544c7cSjsing 	/* Get and check disklabel. */
80b4544c7cSjsing 	if (ioctl(diskfd, DIOCGDINFO, &dl) != 0)
81b4544c7cSjsing 		err(1, "disklabel: %s", dev);
82b4544c7cSjsing 	if (dl.d_magic != DISKMAGIC)
83b4544c7cSjsing 		err(1, "bad disklabel magic=0x%08x", dl.d_magic);
84b4544c7cSjsing 
85b4544c7cSjsing 	/* Warn on unknown disklabel types. */
86b4544c7cSjsing 	if (dl.d_type == 0)
87b4544c7cSjsing 		warnx("disklabel type unknown");
88b4544c7cSjsing 
89b4544c7cSjsing 	/* Determine poffset and set symbol value. */
90b4544c7cSjsing 	pp = &dl.d_partitions[part - 'a'];
91b4544c7cSjsing 	if (pp->p_offseth != 0)
92b4544c7cSjsing 		errx(1, "partition offset too high");
93b4544c7cSjsing 	poffset = pp->p_offset; 		/* Offset of RAID partition. */
94b4544c7cSjsing 	poffset += SR_BOOT_LOADER_OFFSET;	/* SR boot loader area. */
95b4544c7cSjsing 	sym_set_value(pbr_symbols, "_p_offset", poffset);
96b4544c7cSjsing 
97b4544c7cSjsing 	if (verbose)
98b4544c7cSjsing 		fprintf(stderr, "%s%c: installing boot blocks on %s, "
99b4544c7cSjsing 		    "part offset %u\n", bd.bd_vendor, part, dev, poffset);
100b4544c7cSjsing 
101b4544c7cSjsing 	/* Write boot blocks to device. */
102b4544c7cSjsing 	write_bootblocks(diskfd, dev, &dl);
103b4544c7cSjsing 
104b4544c7cSjsing 	close(diskfd);
105b4544c7cSjsing }
106b4544c7cSjsing 
107b4544c7cSjsing void
108b4544c7cSjsing sr_install_bootldr(int devfd, char *dev)
109b4544c7cSjsing {
110b4544c7cSjsing 	struct bioc_installboot bb;
111b4544c7cSjsing 	struct stat sb;
112b4544c7cSjsing 	struct ufs1_dinode *ino_p;
113b4544c7cSjsing 	uint32_t bootsize, inodeblk, inodedbl;
114b4544c7cSjsing 	uint16_t bsize = SR_FS_BLOCKSIZE;
115b4544c7cSjsing 	uint16_t nblocks;
116b4544c7cSjsing 	uint8_t bshift = 5;		/* fragsize == blocksize */
117b4544c7cSjsing 	int fd, i;
118b4544c7cSjsing 	u_char *p;
119b4544c7cSjsing 
120b4544c7cSjsing 	/*
121b4544c7cSjsing 	 * Install boot loader into softraid boot loader storage area.
122b4544c7cSjsing 	 *
123b4544c7cSjsing 	 * In order to allow us to reuse the existing biosboot we construct
124b4544c7cSjsing 	 * a fake FFS filesystem with a single inode, which points to the
125b4544c7cSjsing 	 * boot loader.
126b4544c7cSjsing 	 */
127b4544c7cSjsing 
128b4544c7cSjsing 	nblocks = howmany(SR_BOOT_LOADER_SIZE, SR_FS_BLOCKSIZE / DEV_BSIZE);
129b4544c7cSjsing 	inodeblk = nblocks - 1;
130b4544c7cSjsing 	bootsize = nblocks * SR_FS_BLOCKSIZE;
131b4544c7cSjsing 
132*210008b3Sjsing 	p = calloc(1, bootsize);
133b4544c7cSjsing 	if (p == NULL)
134b4544c7cSjsing 		err(1, NULL);
135b4544c7cSjsing 
136b4544c7cSjsing 	fd = open(stage2, O_RDONLY, 0);
137b4544c7cSjsing 	if (fd == -1)
138b4544c7cSjsing 		err(1, NULL);
139b4544c7cSjsing 
140b4544c7cSjsing 	if (fstat(fd, &sb) == -1)
141b4544c7cSjsing 		err(1, NULL);
142b4544c7cSjsing 
143b4544c7cSjsing 	nblocks = howmany(sb.st_blocks, SR_FS_BLOCKSIZE / DEV_BSIZE);
144b4544c7cSjsing 	if (sb.st_blocks * S_BLKSIZE > bootsize -
145b4544c7cSjsing 	    (int)(sizeof(struct ufs1_dinode)))
146b4544c7cSjsing 		errx(1, "boot code will not fit");
147b4544c7cSjsing 
148b4544c7cSjsing 	/* We only need to fill the direct block array. */
149b4544c7cSjsing 	ino_p = (struct ufs1_dinode *)&p[bootsize - sizeof(struct ufs1_dinode)];
150b4544c7cSjsing 
151b4544c7cSjsing 	ino_p->di_mode = sb.st_mode;
152b4544c7cSjsing 	ino_p->di_nlink = 1;
153b4544c7cSjsing 	ino_p->di_inumber = 0xfeebfaab;
154b4544c7cSjsing 	ino_p->di_size = read(fd, p, sb.st_blocks * S_BLKSIZE);
155b4544c7cSjsing 	ino_p->di_blocks = nblocks;
156b4544c7cSjsing 	for (i = 0; i < nblocks; i++)
157b4544c7cSjsing 		ino_p->di_db[i] = i;
158b4544c7cSjsing 
159b4544c7cSjsing 	inodedbl = ((u_char*)&ino_p->di_db[0] -
160b4544c7cSjsing 	    &p[bootsize - SR_FS_BLOCKSIZE]) + INODEOFF;
161b4544c7cSjsing 
162b4544c7cSjsing 	memset(&bb, 0, sizeof(bb));
163b4544c7cSjsing 	bb.bb_bootldr = p;
164b4544c7cSjsing 	bb.bb_bootldr_size = bootsize;
165b4544c7cSjsing 	bb.bb_bootblk = "XXX";
166b4544c7cSjsing 	bb.bb_bootblk_size = sizeof("XXX");
167b4544c7cSjsing 	strncpy(bb.bb_dev, dev, sizeof(bb.bb_dev));
168b4544c7cSjsing 	if (!nowrite) {
169b4544c7cSjsing 		if (verbose)
170b4544c7cSjsing 			fprintf(stderr, "%s: installing boot loader on "
171b4544c7cSjsing 			    "softraid volume\n", dev);
172b4544c7cSjsing 		if (ioctl(devfd, BIOCINSTALLBOOT, &bb) == -1)
173b4544c7cSjsing 			errx(1, "softraid installboot failed");
174b4544c7cSjsing 	}
175b4544c7cSjsing 
176b4544c7cSjsing 	/*
177b4544c7cSjsing 	 * Set the values that will need to go into biosboot
178b4544c7cSjsing 	 * (the partition boot record, a.k.a. the PBR).
179b4544c7cSjsing 	 */
180b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fs_bsize_p", (bsize / 16));
181b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fs_bsize_s", (bsize / 512));
182b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fsbtodb", bshift);
183b4544c7cSjsing 	sym_set_value(pbr_symbols, "_inodeblk", inodeblk);
184b4544c7cSjsing 	sym_set_value(pbr_symbols, "_inodedbl", inodedbl);
185b4544c7cSjsing 	sym_set_value(pbr_symbols, "_nblocks", nblocks);
186b4544c7cSjsing 
187b4544c7cSjsing 	if (verbose)
188b4544c7cSjsing 		fprintf(stderr, "%s is %d blocks x %d bytes\n",
189b4544c7cSjsing 		    stage2, nblocks, bsize);
190b4544c7cSjsing 
191b4544c7cSjsing 	close(fd);
192b4544c7cSjsing }
193