1/*	$NetBSD: bbinfo.c,v 1.13 2008/05/09 10:53:55 tsutsui Exp $ */
2
3/*-
4 * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette, Paul Kranenburg, and Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if !defined(__lint)
38__RCSID("$NetBSD: bbinfo.c,v 1.13 2008/05/09 10:53:55 tsutsui Exp $");
39#endif	/* !__lint */
40
41#include <sys/param.h>
42#ifndef HAVE_NBTOOL_CONFIG_H
43#include <sys/ioctl.h>
44#include <sys/dkio.h>
45#include <errno.h>
46#endif
47
48#include <assert.h>
49#include <err.h>
50#include <stddef.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56#include "installboot.h"
57
58int
59shared_bbinfo_clearboot(ib_params *params, struct bbinfo_params *bbparams,
60	int (*callback)(ib_params *, struct bbinfo_params *, uint8_t *))
61{
62	uint8_t	*bb;
63	ssize_t	rv;
64	int	retval;
65
66	assert(params != NULL);
67	assert(params->fsfd != -1);
68	assert(params->filesystem != NULL);
69	assert(bbparams != NULL);
70	assert((strlen(bbparams->magic) + 1) == 32);
71
72	retval = 0;
73	if ((bb = malloc(bbparams->maxsize)) == NULL) {
74		warn("Allocating %lu bytes for bbinfo",
75		    (unsigned long) bbparams->maxsize);
76		goto done;
77	}
78
79		/* First check that it _could_ exist here */
80	rv = pread(params->fsfd, bb, bbparams->maxsize, bbparams->offset);
81	if (rv == -1) {
82		warn("Reading `%s'", params->filesystem);
83		goto done;
84	} else if ((uint32_t)rv != bbparams->maxsize) {
85		warnx("Reading `%s': short read", params->filesystem);
86		goto done;
87	}
88
89		/* Now clear out (past the header offset) */
90	memset(bb + bbparams->headeroffset, 0,
91	    bbparams->maxsize - bbparams->headeroffset);
92	if (callback != NULL && ! (*callback)(params, bbparams, bb))
93		goto done;
94
95	if (params->flags & IB_VERBOSE)
96		printf("%slearing boot block\n",
97		    (params->flags & IB_NOWRITE) ? "Not c" : "C");
98	if (params->flags & IB_NOWRITE) {
99		retval = 1;
100		goto done;
101	}
102
103	rv = pwrite(params->fsfd, bb, bbparams->maxsize, bbparams->offset);
104#ifdef DIOCWLABEL
105	if (rv == -1 && errno == EROFS) {
106		/*
107		 * The first sector might be protected by
108		 * bounds_check_with_label(9)
109		 */
110		int enable;
111
112		enable = 1;
113		rv = ioctl(params->fsfd, DIOCWLABEL, &enable);
114		if (rv != 0) {
115			warn("Cannot enable writes to the label sector");
116			goto done;
117		}
118
119		rv = pwrite(params->fsfd, bb, bbparams->maxsize,
120		    bbparams->offset);
121
122		/* Reset write-protect. */
123		enable = 0;
124		(void)ioctl(params->fsfd, DIOCWLABEL, &enable);
125	}
126#endif
127	if (rv == -1) {
128		warn("Writing `%s'", params->filesystem);
129		goto done;
130	} else if ((uint32_t)rv != bbparams->maxsize) {
131		warnx("Writing `%s': short write", params->filesystem);
132		goto done;
133	} else
134		retval = 1;
135
136 done:
137	if (bb != NULL)
138		free(bb);
139	return (retval);
140}
141
142int
143shared_bbinfo_setboot(ib_params *params, struct bbinfo_params *bbparams,
144	int (*callback)(ib_params *, struct bbinfo_params *, uint8_t *))
145{
146	uint8_t		*bb;
147	int		retval;
148	ssize_t		rv;
149	size_t		bbi;
150	struct shared_bbinfo	*bbinfop;	/* bbinfo in prototype image */
151	uint32_t	maxblk, nblk, blk_i;
152	ib_block	*blocks;
153
154	assert(params != NULL);
155	assert(params->fsfd != -1);
156	assert(params->filesystem != NULL);
157	assert(params->fstype != NULL);
158	assert(params->s1fd != -1);
159	assert(params->stage1 != NULL);
160	assert(bbparams != NULL);
161	assert((strlen(bbparams->magic) + 1) == 32);
162
163	bbinfop = NULL;		/* XXXGCC -Wuninitialized [sparc64] */
164	retval = 0;
165	blocks = NULL;
166	if ((bb = malloc(bbparams->maxsize)) == NULL) {
167		warn("Allocating %lu bytes for bbinfo",
168		    (unsigned long) bbparams->maxsize);
169		goto done;
170	}
171
172	if (params->stage2 == NULL) {
173		warnx("Name of secondary bootstrap not provided");
174		goto done;
175	}
176
177	if (params->s1stat.st_size >
178	    bbparams->maxsize - bbparams->headeroffset) {
179		warnx("`%s' cannot be larger than %lu bytes",
180		    params->stage1, (unsigned long)(bbparams->maxsize -
181			bbparams->headeroffset));
182		goto done;
183	}
184
185	memset(bb, 0, bbparams->maxsize);
186	rv = read(params->s1fd, bb + bbparams->headeroffset,
187	    bbparams->maxsize - bbparams->headeroffset);
188	if (rv == -1) {
189		warn("Reading `%s'", params->stage1);
190		goto done;
191	}
192
193		/*
194		 * Quick sanity check that the bootstrap given
195		 * is *not* an ELF executable.
196		 */
197	if (memcmp(bb + bbparams->headeroffset + 1, "ELF", strlen("ELF"))
198	    == 0) {
199		warnx("`%s' is an ELF executable; need raw binary",
200		    params->stage1);
201		goto done;
202	}
203
204#define HOSTTOTARGET32(x) ((bbparams->endian == BBINFO_LITTLE_ENDIAN) \
205			    ? (uint32_t)htole32((x)) : (uint32_t)htobe32((x)))
206#define TARGET32TOHOST(x) ((bbparams->endian == BBINFO_LITTLE_ENDIAN) \
207			    ? (uint32_t)le32toh((x)) : (uint32_t)be32toh((x)))
208
209		/* Look for the bbinfo structure. */
210	bbinfop = NULL;
211	for (bbi = 0; bbi < bbparams->maxsize; bbi += sizeof(uint32_t)) {
212		bbinfop = (void *) (bb + bbparams->headeroffset + bbi);
213		if (memcmp(bbinfop->bbi_magic, bbparams->magic,
214			    sizeof(bbinfop->bbi_magic)) == 0)
215			break;
216	}
217	if (bbi >= bbparams->maxsize) {
218		warnx("%s bbinfo structure not found in `%s'",
219		    params->machine->name, params->stage1);
220		goto done;
221	}
222	maxblk = TARGET32TOHOST(bbinfop->bbi_block_count);
223	if (maxblk == 0 || maxblk > (bbparams->maxsize / sizeof(uint32_t))) {
224		warnx("%s bbinfo structure in `%s' has preposterous size `%u'",
225		    params->machine->name, params->stage1, maxblk);
226		goto done;
227	}
228
229		/* Allocate space for our block list. */
230	blocks = malloc(sizeof(*blocks) * maxblk);
231	if (blocks == NULL) {
232		warn("Allocating %lu bytes",
233		    (unsigned long)sizeof(*blocks) * maxblk);
234		goto done;
235	}
236
237	if (S_ISREG(params->fsstat.st_mode)) {
238		if (fsync(params->fsfd) == -1)
239			warn("Synchronising file system `%s'",
240			    params->filesystem);
241	} else {
242		/* Ensure the secondary bootstrap is on disk. */
243		sync();
244	}
245
246		/* Collect the blocks for the secondary bootstrap. */
247	nblk = maxblk;
248	if (! params->fstype->findstage2(params, &nblk, blocks))
249		goto done;
250	if (nblk == 0) {
251		warnx("Secondary bootstrap `%s' is empty",
252		    params->stage2);
253		goto done;
254	}
255
256		/* Save those blocks in the primary bootstrap. */
257	bbinfop->bbi_block_count = HOSTTOTARGET32(nblk);
258	bbinfop->bbi_block_size = HOSTTOTARGET32(blocks[0].blocksize);
259	for (blk_i = 0; blk_i < nblk; blk_i++) {
260		bbinfop->bbi_block_table[blk_i] =
261		    HOSTTOTARGET32(blocks[blk_i].block);
262		if (blocks[blk_i].blocksize < blocks[0].blocksize &&
263		    blk_i + 1 != nblk) {
264			warnx("Secondary bootstrap `%s' blocks do not have "
265			    "a uniform size", params->stage2);
266			goto done;
267		}
268	}
269	if (callback != NULL && ! (*callback)(params, bbparams, bb))
270		goto done;
271
272	if (params->flags & IB_VERBOSE) {
273		printf("Bootstrap start sector: %u\n",
274		    bbparams->offset / bbparams->blocksize);
275		printf("Bootstrap byte count:   %u\n", (unsigned)rv);
276		printf("Bootstrap block table:  "
277			"%u entries of %u bytes available, %u used:",
278		    maxblk, blocks[0].blocksize, nblk);
279		for (blk_i = 0; blk_i < nblk; blk_i++)
280			printf(" %llu",
281			    (unsigned long long)blocks[blk_i].block);
282		printf("\n%sriting bootstrap\n",
283		    (params->flags & IB_NOWRITE) ? "Not w" : "W");
284	}
285	if (params->flags & IB_NOWRITE) {
286		retval = 1;
287		goto done;
288	}
289
290	rv = pwrite(params->fsfd, bb, bbparams->maxsize, bbparams->offset);
291#ifdef DIOCWLABEL
292	if (rv == -1 && errno == EROFS) {
293		/*
294		 * The first sector might be protected by
295		 * bounds_check_with_label(9)
296		 */
297		int enable;
298
299		enable = 1;
300		rv = ioctl(params->fsfd, DIOCWLABEL, &enable);
301		if (rv != 0) {
302			warn("Cannot enable writes to the label sector");
303			goto done;
304		}
305
306		rv = pwrite(params->fsfd, bb, bbparams->maxsize,
307		    bbparams->offset);
308
309		/* Reset write-protect. */
310		enable = 0;
311		(void)ioctl(params->fsfd, DIOCWLABEL, &enable);
312	}
313#endif
314	if (rv == -1) {
315		warn("Writing `%s'", params->filesystem);
316		goto done;
317	} else if ((uint32_t)rv != bbparams->maxsize) {
318		warnx("Writing `%s': short write", params->filesystem);
319		goto done;
320	} else {
321		retval = 1;
322	}
323
324 done:
325	if (blocks != NULL)
326		free(blocks);
327	if (bb != NULL)
328		free(bb);
329	return (retval);
330}
331