#define ELFSIZE 64
#include <lib/libsa/stand.h>
#include <lib/libkern/funcs.h>
#include <sys/param.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
#include <machine/cpu.h>
#include <lib/libsa/arc4.h>
#ifdef SOFTRAID
#include <sys/queue.h>
#include <dev/biovar.h>
#include <dev/softraidvar.h>
#include <lib/libsa/softraid.h>
#include "disk.h"
#include "softraid_sparc64.h"
#endif
#include "ofdev.h"
#include "openfirm.h"
#ifdef BOOT_DEBUG
uint32_t boot_debug = 0
;
#endif
#define MEG (1024*1024)
char *kernels[] = {
"bsd",
NULL
};
char bootdev[128];
extern char bootfile[128];
int boothowto;
int debug;
char rnddata[BOOTRANDOM_MAX];
struct rc4_ctx randomctx;
int elf64_exec(int, Elf64_Ehdr *, u_int64_t *, void **, void **);
static int
parseargs(char *str, int *howtop)
{
char *cp;
*howtop = 0;
cp = str;
while (*cp == ' ')
++cp;
if (*cp != '-') {
while (*cp && *cp != ' ')
*str++ = *cp++;
while (*cp == ' ')
++cp;
}
*str = 0;
switch (*cp) {
default:
printf("boot options string <%s> must start with -\n", cp);
return -1;
case 0:
return 0;
case '-':
break;
}
++cp;
while (*cp) {
switch (*cp++) {
case 'a':
*howtop |= RB_ASKNAME;
break;
case 'd':
if (!debug) debug = 1;
break;
case 'D':
debug = 2;
break;
}
}
return 0;
}
static void
chain(u_int64_t pentry, char *args, void *ssym, void *esym)
{
extern char end[];
void (*entry)();
int l, machine_tag;
long newargs[3];
entry = (void *)(long)pentry;
l = strlen(args) + 1;
bcopy(&esym, args + l, sizeof(esym));
l += sizeof(esym);
#define SPARC_MACHINE_OPENFIRMWARE 0x44444230
machine_tag = SPARC_MACHINE_OPENFIRMWARE;
bcopy(&machine_tag, args + l, sizeof(machine_tag));
l += sizeof(machine_tag);
newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
newargs[1] = (long)esym;
newargs[2] = (long)ssym;
args = (char *)newargs;
l = sizeof(newargs);
#ifdef DEBUG
printf("chain: calling OF_chain(%p, %p, %x)\n",
entry, args, l);
#endif
if (debug > 1) OF_enter();
OF_chain(entry, args, l);
panic("chain");
}
int
loadfile(int fd, char *args, int isupgrade)
{
union {
Elf64_Ehdr elf64;
} hdr;
int rval;
u_int64_t entry = 0;
void *ssym;
void *esym;
ssym = NULL;
esym = NULL;
#ifdef DEBUG
printf("loadfile: reading header\n");
#endif
if ((rval = read(fd, &hdr, sizeof(hdr))) != sizeof(hdr)) {
if (rval == -1)
printf("read header: %s\n", strerror(errno));
else
printf("read header: short read (only %d of %d)\n",
rval, sizeof(hdr));
rval = 1;
goto err;
}
if (bcmp(hdr.elf64.e_ident, ELFMAG, SELFMAG) == 0 &&
hdr.elf64.e_ident[EI_CLASS] == ELFCLASS64) {
printf("Booting %s\n", opened_name);
rval = elf64_exec(fd, &hdr.elf64, &entry, &ssym, &esym);
} else {
rval = 1;
printf("unknown executable format\n");
}
if (rval)
goto err;
printf(" start=0x%lx\n", (unsigned long)entry);
if (isupgrade) {
struct stat st;
if (fstat(fd, &st) == 0) {
st.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
if (fchmod(fd, st.st_mode) == -1)
printf("fchmod a-x %s: failed\n", opened_name);
}
}
close(fd);
#ifdef SOFTRAID
if (bootdev_dip)
OF_close(bootdev_dip->sr_handle);
sr_clear_keys();
#endif
chain(entry, args, ssym, esym);
err:
close(fd);
return (rval);
}
static int
upgrade(void)
{
struct stat sb;
if (stat("/bsd.upgrade", &sb) < 0)
return 0;
if ((sb.st_mode & S_IXUSR) == 0) {
printf("/bsd.upgrade is not u+x\n");
return 0;
}
return 1;
}
int
loadrandom(char *path, char *buf, size_t buflen)
{
struct stat sb;
int fd, error = 0;
fd = open(path, O_RDONLY);
if (fd == -1)
return -1;
if (fstat(fd, &sb) == -1) {
error = -1;
goto done;
}
if (read(fd, buf, buflen) != buflen) {
error = -1;
goto done;
}
if (sb.st_mode & S_ISTXT) {
printf("NOTE: random seed is being reused.\n");
error = -1;
goto done;
}
fchmod(fd, sb.st_mode | S_ISTXT);
done:
close(fd);
return (error);
}
#ifdef SOFTRAID
static int
srbootdev(const char *bootline)
{
struct sr_boot_volume *bv;
int unit;
bootdev_dip = NULL;
if (bootline[0] == 's' && bootline[1] == 'r' &&
'0' <= bootline[2] && bootline[2] <= '9') {
unit = bootline[2] - '0';
SLIST_FOREACH(bv, &sr_volumes, sbv_link)
if (bv->sbv_unit == unit)
break;
if (bv == NULL) {
printf("Unknown device: sr%d\n", unit);
return ENODEV;
}
} else {
struct sr_boot_chunk *bc;
SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
if ((bv->sbv_flags & BIOC_SCBOOTABLE) == 0)
continue;
SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
struct diskinfo *dip = bc->sbc_diskinfo;
if (!strcmp(dip->path, bootdev))
break;
}
if (bc != NULL)
break;
}
}
if (bv != NULL) {
if ((bv->sbv_flags & BIOC_SCBOOTABLE) == 0) {
printf("device sr%d is not bootable\n", unit);
return ENODEV;
}
if ((bv->sbv_level == 'C' || bv->sbv_level == 0x1C) &&
bv->sbv_keys == NULL)
if (sr_crypto_unlock_volume(bv) != 0)
return EPERM;
if (bv->sbv_diskinfo == NULL) {
struct sr_boot_chunk *bc;
struct diskinfo *dip, *bc_dip;
int sr_handle;
bc = sr_vol_boot_chunk(bv);
if (bc == NULL)
return ENXIO;
bc_dip = (struct diskinfo *)bc->sbc_diskinfo;
sr_handle = OF_open(bc_dip->path);
if (sr_handle == -1)
return EIO;
dip = alloc(sizeof(struct diskinfo));
bzero(dip, sizeof(*dip));
dip->sr_vol = bv;
dip->sr_handle = sr_handle;
bv->sbv_diskinfo = dip;
}
bootdev_dip = bv->sbv_diskinfo;
bv->sbv_part = 'c';
if (sr_getdisklabel(bv, &bootdev_dip->disklabel)) {
OF_close(bootdev_dip->sr_handle);
free(bv->sbv_diskinfo, sizeof(struct diskinfo));
bv->sbv_diskinfo = NULL;
bootdev_dip = NULL;
return ERDLAB;
}
}
return 0;
}
#endif
int
main(void)
{
extern char version[];
int chosen;
int isupgrade = 0;
char bootline[512];
char *cp;
int fd;
#ifdef SOFTRAID
int err;
#endif
char **bootlp;
char *just_bootline[2];
printf(">> OpenBSD BOOT %s\n", version);
if ((chosen = OF_finddevice("/chosen")) == -1 ||
OF_getprop(chosen, "bootpath", bootdev, sizeof bootdev) < 0 ||
OF_getprop(chosen, "bootargs", bootline, sizeof bootline) < 0) {
printf("Invalid Openfirmware environment\n");
exit();
}
#ifdef SOFTRAID
diskprobe();
srprobe();
err = srbootdev(bootline);
if (err) {
printf("Cannot boot from softraid: %s\n", strerror(err));
_rtt();
}
#endif
bootlp = kernels;
if (parseargs(bootline, &boothowto) == -1 ||
(boothowto & RB_ASKNAME)) {
bootlp = 0;
} else if (*bootline) {
just_bootline[0] = bootline;
just_bootline[1] = 0;
bootlp = just_bootline;
} else if (upgrade()) {
just_bootline[0] = "/bsd.upgrade";
just_bootline[1] = 0;
isupgrade = 1;
bootlp = just_bootline;
printf("upgrade detected: switching to %s\n", *bootlp);
}
for (;;) {
if (bootlp) {
cp = *bootlp++;
if (!cp) {
printf("\n");
bootlp = 0;
kernels[0] = 0;
} else if (cp != bootline) {
printf("Trying %s...\n", cp);
if (strlcpy(bootline, cp, sizeof bootline)
>= sizeof bootline) {
printf("bootargs too long: %s\n",
bootline);
_rtt();
}
}
}
if (!bootlp) {
printf("Boot: ");
getln(bootline, sizeof bootline);
if (parseargs(bootline, &boothowto) == -1)
continue;
if (!*bootline) {
bootlp = kernels;
continue;
}
if (strcmp(bootline, "exit") == 0 ||
strcmp(bootline, "halt") == 0) {
_rtt();
}
}
if (loadrandom(BOOTRANDOM, rnddata, sizeof(rnddata)) == 0)
boothowto |= RB_GOODRANDOM;
rc4_keysetup(&randomctx, rnddata, sizeof rnddata);
rc4_skip(&randomctx, 1536);
if ((fd = open(bootline, O_RDONLY)) < 0) {
printf("open %s: %s\n", opened_name, strerror(errno));
continue;
}
#ifdef DEBUG
if (debug)
printf("main: Calling loadfile(fd, %s)\n", opened_name);
#endif
(void)loadfile(fd, opened_name, isupgrade);
}
return 0;
}