#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#if !defined(__lint)
__RCSID("$NetBSD: hp300.c,v 1.22 2025/04/05 19:58:50 tsutsui Exp $");
#endif
#ifdef HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#include <nbinclude/hp300/disklabel.h>
#include <nbinclude/sys/disklabel.h>
#else
#include <sys/disklabel.h>
#endif
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <assert.h>
#include <err.h>
#include <md5.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "installboot.h"
#define HP300_MAXBLOCKS 1
#define LIF_VOLDIRSIZE 1024
#define LIF_PRESERVED (HP300_SECTSIZE * 16)
static int hp300_setboot(ib_params *);
struct ib_mach ib_mach_hp300 = {
.name = "hp300",
.setboot = hp300_setboot,
.clearboot = no_clearboot,
.editboot = no_editboot,
.valid_flags = IB_APPEND,
};
static int
hp300_setboot(ib_params *params)
{
int retval;
uint8_t *bootstrap;
size_t bootstrap_mapsize;
ssize_t rv;
struct partition *boot;
struct hp300_lifdir *lifdir;
int size, offset;
int i;
unsigned int secsize = HP300_SECTSIZE;
uint64_t boot_size, boot_offset;
#ifdef SUPPORT_CD9660
uint32_t nblk;
ib_block *blocks;
#endif
struct disklabel *label;
assert(params != NULL);
assert(params->fsfd != -1);
assert(params->filesystem != NULL);
assert(params->s1fd != -1);
assert(params->stage1 != NULL);
retval = 0;
bootstrap = MAP_FAILED;
bootstrap_mapsize = params->s1stat.st_size;
label = malloc(params->sectorsize);
if (label == NULL) {
warn("Failed to allocate memory for disklabel");
goto done;
}
#ifdef SUPPORT_CD9660
if (params->stage2 != NULL) {
if (strcmp(params->fstype->name, "cd9660") != 0) {
warn("Target filesystem `%s' is unexpected",
params->fstype->name);
}
if (S_ISREG(params->fsstat.st_mode)) {
if (fsync(params->fsfd) == -1)
warn("Synchronising file system `%s'",
params->filesystem);
} else {
warnx("`%s' must be a regular file to append "
"a bootstrap", params->filesystem);
goto done;
}
nblk = HP300_MAXBLOCKS;
blocks = malloc(sizeof(*blocks) * nblk);
if (blocks == NULL) {
warn("Allocating %lu bytes for block list",
(unsigned long)sizeof(*blocks) * nblk);
goto done;
}
if (!params->fstype->findstage2(params, &nblk, blocks))
goto done;
if (nblk == 0) {
warnx("Secondary bootstrap `%s' is empty",
params->stage2);
goto done;
} else if (nblk > 1) {
warnx("Secondary bootstrap `%s' doesn't have "
"contiguous blocks", params->stage2);
goto done;
}
boot_offset = blocks[0].block * params->fstype->blocksize;
bootstrap_mapsize = LIF_VOLDIRSIZE;
if ((params->flags & IB_VERBOSE) != 0) {
printf("Bootstrap `%s' found at offset %lu in `%s'\n",
params->stage2, (unsigned long)boot_offset,
params->filesystem);
}
} else
#endif
if ((params->flags & IB_APPEND) != 0) {
if (!S_ISREG(params->fsstat.st_mode)) {
warnx(
"`%s' must be a regular file to append a bootstrap",
params->filesystem);
goto done;
}
boot_offset = roundup(params->fsstat.st_size, HP300_SECTSIZE);
} else {
if (pread(params->fsfd, label, params->sectorsize,
LABELSECTOR * params->sectorsize)
!= (ssize_t)params->sectorsize) {
warn("reading disklabel");
goto done;
}
secsize = be32toh(label->d_secsize);
if (label->d_magic != htobe32(DISKMAGIC) ||
label->d_magic2 != htobe32(DISKMAGIC) ||
secsize == 0 || !powerof2(secsize) ||
be16toh(label->d_npartitions) > MAXMAXPARTITIONS) {
warnx("Invalid disklabel in %s", params->filesystem);
goto done;
}
i = be16toh(label->d_npartitions);
for (boot = label->d_partitions; ; boot++) {
if (--i < 0) {
warnx("No BOOT partition");
goto done;
}
if (boot->p_fstype == FS_BOOT)
break;
}
boot_size = be32toh(boot->p_size) * (uint64_t)secsize;
boot_offset = be32toh(boot->p_offset) * (uint64_t)secsize;
if (boot_size < (uint64_t)params->s1stat.st_size) {
warn("BOOT partition too small (%llu < %llu)",
(unsigned long long)boot_size,
(unsigned long long)params->s1stat.st_size);
goto done;
}
}
bootstrap = mmap(NULL, bootstrap_mapsize,
PROT_READ | PROT_WRITE, MAP_PRIVATE, params->s1fd, 0);
if (bootstrap == MAP_FAILED) {
warn("mmapping `%s'", params->stage1);
goto done;
}
lifdir = (void *)(bootstrap + HP300_LIF_DIRSTART);
for (i = 0; i < HP300_LIF_NUMDIR; i++) {
uint32_t addr = be32toh(lifdir[i].dir_addr);
uint32_t limit = hp300_btolifs(params->s1stat.st_size);
uint32_t end = addr + be32toh(lifdir[i].dir_length);
if (end > limit) {
warnx("LIF entry %d larger (%u %u) than LIF file",
i, end, limit);
goto done;
}
if (addr != 0 && boot_offset != 0)
lifdir[i].dir_addr =
htobe32(boot_offset / HP300_SECTSIZE + addr);
}
if ((params->flags & IB_NOWRITE) != 0) {
retval = 1;
goto done;
}
rv = pwrite(params->fsfd, bootstrap, LIF_VOLDIRSIZE, 0);
if (rv != LIF_VOLDIRSIZE) {
if (rv == -1)
warn("Writing `%s'", params->filesystem);
else
warnx("Writing `%s': short write", params->filesystem);
goto done;
}
#ifdef SUPPORT_CD9660
if (params->stage2 != NULL) {
retval = 1;
goto done;
}
#endif
offset = (boot_offset <= LIF_PRESERVED) ? LIF_PRESERVED : 0;
size = roundup(params->s1stat.st_size, secsize) - offset;
rv = pwrite(params->fsfd, bootstrap + offset, size,
boot_offset + offset);
if (rv != size) {
if (rv == -1)
warn("Writing boot filesystem of `%s'",
params->filesystem);
else
warnx("Writing boot filesystem of `%s': short write",
params->filesystem);
goto done;
}
retval = 1;
done:
if (label != NULL)
free(label);
if (bootstrap != MAP_FAILED)
munmap(bootstrap, bootstrap_mapsize);
return retval;
}