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