1 /* $NetBSD: hp300.c,v 1.22 2025/04/05 19:58:50 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.22 2025/04/05 19:58:50 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/hp300/disklabel.h>
45 #include <nbinclude/sys/disklabel.h>
46 #else
47 #include <sys/disklabel.h>
48 #endif
49 #include <sys/fcntl.h>
50 #include <sys/ioctl.h>
51 #include <sys/mman.h>
52 #include <sys/param.h>
53 
54 #include <assert.h>
55 #include <err.h>
56 #include <md5.h>
57 #include <stddef.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #include "installboot.h"
64 
65 #define HP300_MAXBLOCKS       1         /* Only contiguous blocks are expected. */
66 #define LIF_VOLDIRSIZE        1024      /* size of LIF volume header and directory */
67 
68 /* XXX: should be HP300_LIF_FILESTART? */
69 #define LIF_PRESERVED         (HP300_SECTSIZE * 16)
70 
71 static int hp300_setboot(ib_params *);
72 
73 struct ib_mach ib_mach_hp300 = {
74           .name               =         "hp300",
75           .setboot  =         hp300_setboot,
76           .clearboot          =         no_clearboot,
77           .editboot =         no_editboot,
78           .valid_flags        =         IB_APPEND,
79 };
80 
81 static int
hp300_setboot(ib_params * params)82 hp300_setboot(ib_params *params)
83 {
84           int                 retval;
85           uint8_t             *bootstrap;
86           size_t              bootstrap_mapsize;
87           ssize_t             rv;
88           struct partition *boot;
89           struct hp300_lifdir *lifdir;
90           int                 size, offset;
91           int                 i;
92           unsigned int        secsize = HP300_SECTSIZE;
93           uint64_t  boot_size, boot_offset;
94 #ifdef SUPPORT_CD9660
95           uint32_t  nblk;
96           ib_block  *blocks;
97 #endif
98           struct disklabel *label;
99 
100           assert(params != NULL);
101           assert(params->fsfd != -1);
102           assert(params->filesystem != NULL);
103           assert(params->s1fd != -1);
104           assert(params->stage1 != NULL);
105 
106           retval = 0;
107           bootstrap = MAP_FAILED;
108 
109           /* needs whole LIF volume/directory and actual bootstrap by default */
110           bootstrap_mapsize = params->s1stat.st_size;
111 
112           label = malloc(params->sectorsize);
113           if (label == NULL) {
114                     warn("Failed to allocate memory for disklabel");
115                     goto done;
116           }
117 
118 #ifdef SUPPORT_CD9660
119           if (params->stage2 != NULL) {
120                     /*
121                      * Use contiguous blocks of SYS_BOOT in the target filesystem
122                      * (assuming ISO9660) for a LIF directory entry used
123                      * by BOOTROM on bootstrap.
124                      */
125                     if (strcmp(params->fstype->name, "cd9660") != 0) {
126                               warn("Target filesystem `%s' is unexpected",
127                                   params->fstype->name);
128                     }
129 
130                     if (S_ISREG(params->fsstat.st_mode)) {
131                               if (fsync(params->fsfd) == -1)
132                                         warn("Synchronising file system `%s'",
133                                             params->filesystem);
134                     } else {
135                               /* Don't allow real file systems for sanity */
136                               warnx("`%s' must be a regular file to append "
137                                   "a bootstrap", params->filesystem);
138                               goto done;
139                     }
140 
141                     /* Allocate space for our block list. */
142                     nblk = HP300_MAXBLOCKS;
143                     blocks = malloc(sizeof(*blocks) * nblk);
144                     if (blocks == NULL) {
145                               warn("Allocating %lu bytes for block list",
146                                   (unsigned long)sizeof(*blocks) * nblk);
147                               goto done;
148                     }
149 
150                     /* Check the block of for the SYS_UBOOT in the target fs */
151                     if (!params->fstype->findstage2(params, &nblk, blocks))
152                               goto done;
153 
154                     if (nblk == 0) {
155                               warnx("Secondary bootstrap `%s' is empty",
156                                   params->stage2);
157                               goto done;
158                     } else if (nblk > 1) {
159                               warnx("Secondary bootstrap `%s' doesn't have "
160                                   "contiguous blocks", params->stage2);
161                               goto done;
162                     }
163 
164                     boot_offset = blocks[0].block * params->fstype->blocksize;
165                     /* need to read only LIF volume and directories */
166                     bootstrap_mapsize = LIF_VOLDIRSIZE;
167 
168                     if ((params->flags & IB_VERBOSE) != 0) {
169                               printf("Bootstrap `%s' found at offset %lu in `%s'\n",
170                                   params->stage2, (unsigned long)boot_offset,
171                                   params->filesystem);
172                     }
173 
174           } else
175 #endif
176           if ((params->flags & IB_APPEND) != 0) {
177                     if (!S_ISREG(params->fsstat.st_mode)) {
178                               warnx(
179                         "`%s' must be a regular file to append a bootstrap",
180                                   params->filesystem);
181                               goto done;
182                     }
183                     boot_offset = roundup(params->fsstat.st_size, HP300_SECTSIZE);
184           } else {
185                     /*
186                      * The bootstrap can be well over 8k, and must go into a BOOT
187                      * partition. Read NetBSD label to locate BOOT partition.
188                      */
189                     if (pread(params->fsfd, label, params->sectorsize,
190                         LABELSECTOR * params->sectorsize)
191                         != (ssize_t)params->sectorsize) {
192                               warn("reading disklabel");
193                               goto done;
194                     }
195                     /* And a quick validation - must be a big-endian label */
196                     secsize = be32toh(label->d_secsize);
197                     if (label->d_magic != htobe32(DISKMAGIC) ||
198                         label->d_magic2 != htobe32(DISKMAGIC) ||
199                         secsize == 0 || !powerof2(secsize) ||
200                         be16toh(label->d_npartitions) > MAXMAXPARTITIONS) {
201                               warnx("Invalid disklabel in %s", params->filesystem);
202                               goto done;
203                     }
204 
205                     i = be16toh(label->d_npartitions);
206                     for (boot = label->d_partitions; ; boot++) {
207                               if (--i < 0) {
208                                         warnx("No BOOT partition");
209                                         goto done;
210                               }
211                               if (boot->p_fstype == FS_BOOT)
212                                         break;
213                     }
214                     boot_size = be32toh(boot->p_size) * (uint64_t)secsize;
215                     boot_offset = be32toh(boot->p_offset) * (uint64_t)secsize;
216 
217                     /*
218                      * We put the entire LIF file into the BOOT partition even when
219                      * it doesn't start at the beginning of the disk.
220                      *
221                      * Maybe we ought to be able to take a binary file and add
222                      * it to the LIF filesystem.
223                      */
224                     if (boot_size < (uint64_t)params->s1stat.st_size) {
225                               warn("BOOT partition too small (%llu < %llu)",
226                                         (unsigned long long)boot_size,
227                                         (unsigned long long)params->s1stat.st_size);
228                               goto done;
229                     }
230           }
231 
232           /* Read LIF volume/directory (and bootstrap) from stage1 */
233           bootstrap = mmap(NULL, bootstrap_mapsize,
234               PROT_READ | PROT_WRITE, MAP_PRIVATE, params->s1fd, 0);
235           if (bootstrap == MAP_FAILED) {
236                     warn("mmapping `%s'", params->stage1);
237                     goto done;
238           }
239 
240           /* Relocate files, sanity check LIF directory on the way */
241           lifdir = (void *)(bootstrap + HP300_LIF_DIRSTART);
242           for (i = 0; i < HP300_LIF_NUMDIR; i++) {
243                     uint32_t addr = be32toh(lifdir[i].dir_addr);
244                     uint32_t limit = hp300_btolifs(params->s1stat.st_size);
245                     uint32_t end = addr + be32toh(lifdir[i].dir_length);
246                     if (end > limit) {
247                               warnx("LIF entry %d larger (%u %u) than LIF file",
248                                   i, end, limit);
249                               goto done;
250                     }
251                     if (addr != 0 && boot_offset != 0)
252                               lifdir[i].dir_addr =
253                                   htobe32(boot_offset / HP300_SECTSIZE + addr);
254           }
255 
256           if ((params->flags & IB_NOWRITE) != 0) {
257                     retval = 1;
258                     goto done;
259           }
260 
261           /* Write LIF volume header and directory to sectors 0 and 1 */
262           rv = pwrite(params->fsfd, bootstrap, LIF_VOLDIRSIZE, 0);
263           if (rv != LIF_VOLDIRSIZE) {
264                     if (rv == -1)
265                               warn("Writing `%s'", params->filesystem);
266                     else
267                               warnx("Writing `%s': short write", params->filesystem);
268                     goto done;
269           }
270 
271 #ifdef SUPPORT_CD9660
272           if (params->stage2 != NULL) {
273                     /*
274                      * Bootstrap in the target filesystem is used.
275                      * No need to write bootstrap to BOOT partition.
276                      */
277                     retval = 1;
278                     goto done;
279           }
280 #endif
281 
282           /* Write bootstrap to BOOT partition (or at EOF in IB_APPEND case) */
283           /* LIF header and directories, and disklabel should be preserved */
284           offset = (boot_offset <= LIF_PRESERVED) ? LIF_PRESERVED : 0;
285           size = roundup(params->s1stat.st_size, secsize) - offset;
286 
287           rv = pwrite(params->fsfd, bootstrap + offset, size,
288               boot_offset + offset);
289           if (rv != size) {
290                     if (rv == -1)
291                               warn("Writing boot filesystem of `%s'",
292                                   params->filesystem);
293                     else
294                               warnx("Writing boot filesystem of `%s': short write",
295                                   params->filesystem);
296                     goto done;
297           }
298 
299           retval = 1;
300 
301  done:
302           if (label != NULL)
303                     free(label);
304           if (bootstrap != MAP_FAILED)
305                     munmap(bootstrap, bootstrap_mapsize);
306           return retval;
307 }
308