1/* $NetBSD: hp300.c,v 1.12 2010/01/07 13:26:00 tsutsui Exp $ */
2
3/*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by David Laight.
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: hp300.c,v 1.12 2010/01/07 13:26:00 tsutsui Exp $");
39#endif /* !__lint */
40
41/* We need the target disklabel.h, not the hosts one..... */
42#ifdef HAVE_NBTOOL_CONFIG_H
43#include "nbtool_config.h"
44#include <nbinclude/sys/disklabel.h>
45#else
46#include <sys/disklabel.h>
47#endif
48#include <sys/fcntl.h>
49#include <sys/ioctl.h>
50#include <sys/mman.h>
51#include <sys/param.h>
52
53#include <assert.h>
54#include <err.h>
55#include <md5.h>
56#include <stddef.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61
62#include "installboot.h"
63
64static int hp300_setboot(ib_params *);
65
66struct ib_mach ib_mach_hp300 =
67	{ "hp300", hp300_setboot, no_clearboot, no_editboot, IB_APPEND };
68
69static int
70hp300_setboot(ib_params *params)
71{
72	int		retval;
73	uint8_t		*bootstrap;
74	ssize_t		rv;
75	struct partition *boot;
76	struct hp300_lifdir *lifdir;
77	int		offset;
78	int		i;
79	unsigned int	secsize = HP300_SECTSIZE;
80	uint64_t	boot_size, boot_offset;
81	struct disklabel *label;
82
83	assert(params != NULL);
84	assert(params->fsfd != -1);
85	assert(params->filesystem != NULL);
86	assert(params->s1fd != -1);
87	assert(params->stage1 != NULL);
88
89	retval = 0;
90	bootstrap = MAP_FAILED;
91
92	label = malloc(params->sectorsize);
93	if (label == NULL) {
94		warn("Failed to allocate memory for disklabel");
95		goto done;
96	}
97
98	if (params->flags & IB_APPEND) {
99		if (!S_ISREG(params->fsstat.st_mode)) {
100			warnx(
101		    "`%s' must be a regular file to append a bootstrap",
102			    params->filesystem);
103			goto done;
104		}
105		boot_offset = roundup(params->fsstat.st_size, HP300_SECTSIZE);
106	} else {
107		/*
108		 * The bootstrap can be well over 8k, and must go into a BOOT
109		 * partition. Read NetBSD label to locate BOOT partition.
110		 */
111		if (pread(params->fsfd, label, params->sectorsize,
112		    LABELSECTOR * params->sectorsize)
113		    != (ssize_t)params->sectorsize) {
114			warn("reading disklabel");
115			goto done;
116		}
117		/* And a quick validation - must be a big-endian label */
118		secsize = be32toh(label->d_secsize);
119		if (label->d_magic != htobe32(DISKMAGIC) ||
120		    label->d_magic2 != htobe32(DISKMAGIC) ||
121		    secsize == 0 || secsize & (secsize - 1) ||
122		    be16toh(label->d_npartitions) > MAXMAXPARTITIONS) {
123			warnx("Invalid disklabel in %s", params->filesystem);
124			goto done;
125		}
126
127		i = be16toh(label->d_npartitions);
128		for (boot = label->d_partitions; ; boot++) {
129			if (--i < 0) {
130				warnx("No BOOT partition");
131				goto done;
132			}
133			if (boot->p_fstype == FS_BOOT)
134				break;
135		}
136		boot_size = be32toh(boot->p_size) * (uint64_t)secsize;
137		boot_offset = be32toh(boot->p_offset) * (uint64_t)secsize;
138
139		/*
140		 * We put the entire LIF file into the BOOT partition even when
141		 * it doesn't start at the beginning of the disk.
142		 *
143		 * Maybe we ought to be able to take a binary file and add
144		 * it to the LIF filesystem.
145		 */
146		if (boot_size < (uint64_t)params->s1stat.st_size) {
147			warn("BOOT partition too small (%llu < %llu)",
148				(unsigned long long)boot_size,
149				(unsigned long long)params->s1stat.st_size);
150			goto done;
151		}
152	}
153
154	bootstrap = mmap(NULL, params->s1stat.st_size, PROT_READ | PROT_WRITE,
155			    MAP_PRIVATE, params->s1fd, 0);
156	if (bootstrap == MAP_FAILED) {
157		warn("mmaping `%s'", params->stage1);
158		goto done;
159	}
160
161	/* Relocate files, sanity check LIF directory on the way */
162	lifdir = (void *)(bootstrap + HP300_SECTSIZE * 2);
163	for (i = 0; i < 8; lifdir++, i++) {
164		int32_t addr = be32toh(lifdir->dir_addr);
165		int32_t limit = (params->s1stat.st_size - 1) / HP300_SECTSIZE + 1;
166		int32_t end = addr + be32toh(lifdir->dir_length);
167		if (end > limit) {
168			warnx("LIF entry %d larger (%d %d) than LIF file",
169				i, end, limit);
170			goto done;
171		}
172		if (addr != 0 && boot_offset != 0)
173			lifdir->dir_addr = htobe32(addr + boot_offset
174							    / HP300_SECTSIZE);
175	}
176
177	if (params->flags & IB_NOWRITE) {
178		retval = 1;
179		goto done;
180	}
181
182	/* Write LIF volume header and directory to sectors 0 and 1 */
183	rv = pwrite(params->fsfd, bootstrap, 1024, 0);
184	if (rv != 1024) {
185		if (rv == -1)
186			warn("Writing `%s'", params->filesystem);
187		else
188			warnx("Writing `%s': short write", params->filesystem);
189		goto done;
190	}
191
192	/* Write files to BOOT partition */
193	offset = boot_offset <= HP300_SECTSIZE * 16 ? HP300_SECTSIZE * 16 : 0;
194	i = roundup(params->s1stat.st_size, secsize) - offset;
195	rv = pwrite(params->fsfd, bootstrap + offset, i, boot_offset + offset);
196	if (rv != i) {
197		if (rv == -1)
198			warn("Writing boot filesystem of `%s'",
199				params->filesystem);
200		else
201			warnx("Writing boot filesystem of `%s': short write",
202				params->filesystem);
203		goto done;
204	}
205
206	retval = 1;
207
208 done:
209	if (label != NULL)
210		free(label);
211	if (bootstrap != MAP_FAILED)
212		munmap(bootstrap, params->s1stat.st_size);
213	return retval;
214}
215