436 lines
11 KiB
C
436 lines
11 KiB
C
/* $NetBSD: main.c,v 1.1 2014/02/24 07:23:43 skrll Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2003 ITOH Yasufumi.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary forms are unlimited.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <ufs/ufs/dinode.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/exec_elf.h>
|
|
#include "readufs.h"
|
|
|
|
#define STACK_SIZE ((unsigned) (64*1024))
|
|
#define LOAD_ALIGN ((unsigned) 2048)
|
|
|
|
#define PZ_MEM_BOOT 0x3d0
|
|
#define DEV_CLASS 0x2c
|
|
#define DEV_CL_MASK 0xf
|
|
#define DEV_CL_SEQU 0x2 /* sequential record access media */
|
|
|
|
static char *hexstr(char *, unsigned);
|
|
void ipl_main(unsigned /*interactive*/, unsigned /*sptop*/, unsigned /*psw*/);
|
|
void load_file(const char *, uintptr_t /*loadadr*/, unsigned /*interactive*/,
|
|
int /*part*/);
|
|
void load_file_ino(ino32_t, const char *, uintptr_t /*loadadr*/,
|
|
unsigned /*interactive*/, int /*part*/);
|
|
|
|
struct loadinfo {
|
|
void *sec_image;
|
|
unsigned sec_size;
|
|
#if 0
|
|
unsigned sec_pad;
|
|
#endif
|
|
unsigned entry_offset;
|
|
};
|
|
static inline void xi_elf32(struct loadinfo *, Elf32_Ehdr *);
|
|
static inline void xi_elf64(struct loadinfo *, Elf64_Ehdr *);
|
|
int xi_load(struct loadinfo *, void *);
|
|
|
|
void reboot(void);
|
|
void halt(void);
|
|
void dispatch(unsigned /*interactive*/, unsigned /*top*/,
|
|
unsigned /*end*/, int /*part*/, unsigned /*entry*/);
|
|
void print(const char *);
|
|
void putch(int);
|
|
int getch(void);
|
|
int boot_input(void *, int /*len*/, int /*pos*/);
|
|
|
|
/* to make generated code relocatable, do NOT mark them as const */
|
|
extern char str_seekseq[], str_bit_firmware[];
|
|
extern char str_crlf[], str_space[], str_rubout[];
|
|
extern char str_bootpart[], str_booting_part[];
|
|
extern char str_warn_2GB[], str_warn_unused[], str_nolabel[];
|
|
extern char str_filesystem[], str_nofs[];
|
|
extern char str_lookup[], str_loading[], str_at[], str_dddot[], str_done[];
|
|
extern char str_boot1[], str_boot2[], str_boot3[];
|
|
extern char str_noboot[];
|
|
extern char str_ukfmt[];
|
|
|
|
#ifdef __GNUC__
|
|
#define memcpy(d, s, n) __builtin_memcpy(d, s, n)
|
|
#else
|
|
void *memcpy(void *, const void *, size_t);
|
|
#endif
|
|
void *memmove(void *, const void *, size_t);
|
|
|
|
/* disklabel */
|
|
union {
|
|
char dklsec[512];
|
|
struct disklabel dkl; /* to ensure alignment */
|
|
} labelsector;
|
|
#define dklabel (*(struct disklabel *)(labelsector.dklsec + LABELOFFSET))
|
|
|
|
unsigned offset_raw_read;
|
|
|
|
extern char diskbuf[2048];
|
|
#define BLK_PER_READ 4
|
|
#define MASK_BLK_PER_READ (BLK_PER_READ - 1)
|
|
|
|
void
|
|
RAW_READ(void *buf, daddr_t blkpos, size_t bytelen)
|
|
{
|
|
char *b = buf;
|
|
size_t off, readlen;
|
|
int devoff;
|
|
static int prvdevoff = -dbtob(BLK_PER_READ);
|
|
int pos;
|
|
|
|
for ( ; bytelen > 0; b += readlen, bytelen -= readlen) {
|
|
/*
|
|
* read 2KB, avoiding unneeded read
|
|
*/
|
|
devoff = dbtob(blkpos & ~MASK_BLK_PER_READ) + offset_raw_read;
|
|
if (prvdevoff != devoff) {
|
|
#if 1 /* supports sequential media */
|
|
if ((*(unsigned *)(PZ_MEM_BOOT+DEV_CLASS) & DEV_CL_MASK)
|
|
== DEV_CL_SEQU) {
|
|
/*
|
|
* sequential media
|
|
* -- read sequentially or rewind
|
|
*/
|
|
pos = prvdevoff + dbtob(BLK_PER_READ);
|
|
if (devoff < pos)
|
|
pos = 0; /* rewind */
|
|
|
|
/* "repositioning media...\r\n" */
|
|
if (devoff - pos > 512 * 1024)
|
|
print(str_seekseq);
|
|
|
|
for (; pos < devoff; pos += dbtob(BLK_PER_READ))
|
|
boot_input(diskbuf,
|
|
dbtob(BLK_PER_READ), pos);
|
|
}
|
|
#endif
|
|
prvdevoff = devoff;
|
|
boot_input(diskbuf, dbtob(BLK_PER_READ), devoff);
|
|
}
|
|
/*
|
|
* copy specified size to the destination
|
|
*/
|
|
off = dbtob(blkpos & MASK_BLK_PER_READ),
|
|
readlen = dbtob(BLK_PER_READ) - off;
|
|
if (readlen > bytelen)
|
|
readlen = bytelen;
|
|
memcpy(b, diskbuf + off, readlen);
|
|
blkpos = (blkpos & ~MASK_BLK_PER_READ) + BLK_PER_READ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* convert number to hex string
|
|
* buf must have enough space
|
|
*/
|
|
static char *
|
|
hexstr(char *buf, unsigned val)
|
|
{
|
|
unsigned v;
|
|
char rev[16];
|
|
char *r = rev, *b = buf;
|
|
|
|
/* inverse order */
|
|
do {
|
|
v = val & 0xf;
|
|
*r++ = (v <= 9) ? '0' + v : 'a' - 10 + v;
|
|
val >>= 4;
|
|
} while (val);
|
|
|
|
/* reverse string */
|
|
while (r > rev)
|
|
*b++ = *--r;
|
|
|
|
*b = '\0';
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
ipl_main(unsigned interactive, unsigned sptop, unsigned psw)
|
|
/* interactive: parameters from PDC */
|
|
/* sptop: value of sp on function entry */
|
|
/* psw: PSW on startup */
|
|
{
|
|
char buf[32];
|
|
int part = 0; /* default partition "a" */
|
|
unsigned secsz, partoff, partsz;
|
|
int c, c1;
|
|
uintptr_t loadadr;
|
|
|
|
#if 0
|
|
print(hexstr(buf, interactive));
|
|
print(str_crlf);
|
|
print(hexstr(buf, sptop));
|
|
print(str_crlf);
|
|
print(hexstr(buf, psw));
|
|
print(str_crlf);
|
|
#endif
|
|
|
|
print(hexstr(buf, (psw & 0x08000000) ? (unsigned) 0x64 : 0x32));
|
|
print(str_bit_firmware); /* "bit firmware\r\n" */
|
|
|
|
/*
|
|
* check disklabel
|
|
* (dklabel has disklabel on startup)
|
|
*/
|
|
if (dklabel.d_magic == DISKMAGIC && (secsz = dklabel.d_secsize) != 0) {
|
|
/*
|
|
* select boot partition
|
|
*/
|
|
if (interactive) {
|
|
select_partition:
|
|
/* "boot partition (a-p, ! to reboot) [a]:" */
|
|
print(str_bootpart);
|
|
part = 0; /* default partition "a" */
|
|
c1 = 0;
|
|
while ((c = getch()) >= 0) {
|
|
switch (c) {
|
|
case '\n':
|
|
case '\r':
|
|
goto break_while;
|
|
case '\b':
|
|
case '\177':
|
|
if (c1) {
|
|
print(str_rubout);
|
|
part = c1 = 0;
|
|
}
|
|
break;
|
|
case '!': /* reset */
|
|
if (c1 == 0) {
|
|
part = -1;
|
|
goto echoback;
|
|
}
|
|
break;
|
|
default:
|
|
if (c1 == 0 && c >= 'a' && c <= 'p') {
|
|
part = c - 'a';
|
|
echoback:
|
|
putch(c);
|
|
c1 = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break_while:
|
|
if (part == -1)
|
|
return; /* reset */
|
|
}
|
|
|
|
/*
|
|
* "\r\nbooting from partition _\r\n"
|
|
*/
|
|
str_booting_part[25] = 'a' + part;
|
|
print(str_booting_part);
|
|
|
|
partoff = dklabel.d_partitions[part].p_offset;
|
|
partsz = dklabel.d_partitions[part].p_size;
|
|
|
|
if (part >= (int) dklabel.d_npartitions || partsz == 0) {
|
|
print(str_warn_unused); /* "unused partition\r\n" */
|
|
goto select_partition;
|
|
}
|
|
|
|
/* boot partition must be below 2GB */
|
|
if (partoff + partsz > ((unsigned)2*1024*1024*1024) / secsz) {
|
|
/* "boot partition exceeds 2GB boundary\r\n" */
|
|
print(str_warn_2GB);
|
|
goto select_partition;
|
|
}
|
|
|
|
/*
|
|
* following device accesses are only in the partition
|
|
*/
|
|
offset_raw_read = partoff * secsz;
|
|
} else {
|
|
/*
|
|
* no disklabel --- assume the whole of the device
|
|
* is a filesystem
|
|
*/
|
|
print(str_nolabel); /* "no disklabel\r\n" */
|
|
}
|
|
|
|
if (ufs_init()) {
|
|
print(str_nofs); /* "no filesystem found\r\n" */
|
|
return;
|
|
}
|
|
str_filesystem[12] = (ufs_info.fstype == UFSTYPE_FFS) ? 'F' : 'L';
|
|
print(str_filesystem); /* "filesystem: _FS\r\n" */
|
|
|
|
loadadr = (sptop + STACK_SIZE + LOAD_ALIGN - 1) & (-LOAD_ALIGN);
|
|
load_file(str_boot1, loadadr, interactive, part); /* "boot.hp700" */
|
|
load_file(str_boot2, loadadr, interactive, part); /* "boot" */
|
|
load_file(str_boot3, loadadr, interactive, part); /* "usr/mdec/boot" */
|
|
|
|
print(str_noboot); /* "no secondary boot found\r\n" */
|
|
}
|
|
|
|
void
|
|
load_file(const char *path, uintptr_t loadadr, unsigned interactive, int part)
|
|
{
|
|
|
|
/* look-up the file */
|
|
print(str_lookup); /* "looking up " */
|
|
print(path);
|
|
print(str_crlf);
|
|
load_file_ino(ufs_lookup_path(path), path, loadadr, interactive, part);
|
|
}
|
|
|
|
void
|
|
load_file_ino(ino32_t ino, const char *fn, uintptr_t loadadr, unsigned interactive, int part)
|
|
/* fn: for message only */
|
|
{
|
|
union ufs_dinode dinode;
|
|
size_t sz;
|
|
struct loadinfo inf;
|
|
char buf[32];
|
|
|
|
if (ino == 0 || ufs_get_inode(ino, &dinode))
|
|
return; /* not found */
|
|
|
|
print(str_loading); /* "loading " */
|
|
print(fn);
|
|
print(str_at); /* " at 0x" */
|
|
print(hexstr(buf, loadadr));
|
|
print(str_dddot); /* "..." */
|
|
|
|
sz = DI_SIZE(&dinode);
|
|
ufs_read(&dinode, (void *) loadadr, 0, sz);
|
|
|
|
print(str_done); /* "done\r\n" */
|
|
|
|
/* digest executable format */
|
|
inf.sec_size = sz;
|
|
inf.entry_offset = 0;
|
|
if (xi_load(&inf, (void *) loadadr)) {
|
|
print(fn);
|
|
print(str_ukfmt); /* ": unknown format -- exec from top\r\n" */
|
|
}
|
|
|
|
/* pass control to the secondary boot */
|
|
dispatch(interactive, loadadr, loadadr + inf.sec_size, part,
|
|
loadadr + inf.entry_offset);
|
|
}
|
|
|
|
/*
|
|
* fill in loading information from an ELF executable
|
|
*/
|
|
static inline void
|
|
xi_elf32(struct loadinfo *inf, Elf32_Ehdr *hdr)
|
|
{
|
|
char *top = (void *) hdr;
|
|
Elf32_Phdr *ph;
|
|
|
|
/* text + data, bss */
|
|
ph = (void *) (top + hdr->e_phoff);
|
|
inf->sec_image = top + ph->p_offset;
|
|
inf->sec_size = ph->p_filesz;
|
|
#if 0
|
|
inf->sec_pad = ph->p_memsz - ph->p_filesz;
|
|
#endif
|
|
/* entry */
|
|
inf->entry_offset = hdr->e_entry - ph->p_vaddr;
|
|
}
|
|
|
|
static inline void
|
|
xi_elf64(struct loadinfo *inf, Elf64_Ehdr *hdr)
|
|
{
|
|
char *top = (void *) hdr;
|
|
Elf64_Phdr *ph;
|
|
|
|
/*
|
|
* secondary boot is not so large, so 32bit (unsigned) arithmetic
|
|
* is enough
|
|
*/
|
|
/* text + data, bss */
|
|
ph = (void *) (top + (unsigned) hdr->e_phoff);
|
|
inf->sec_image = top + (unsigned) ph->p_offset;
|
|
inf->sec_size = (unsigned) ph->p_filesz;
|
|
#if 0
|
|
inf->sec_pad = (unsigned) ph->p_memsz - (unsigned) ph->p_filesz;
|
|
#endif
|
|
/* entry */
|
|
inf->entry_offset = (unsigned) hdr->e_entry - (unsigned) ph->p_vaddr;
|
|
}
|
|
|
|
int
|
|
xi_load(struct loadinfo *inf, void *buf)
|
|
{
|
|
Elf32_Ehdr *e32hdr = buf;
|
|
Elf64_Ehdr *e64hdr = buf;
|
|
uint16_t class_data;
|
|
|
|
/*
|
|
* check ELF header
|
|
* (optimized assuming big endian byte order)
|
|
*/
|
|
/* ELF magic */
|
|
if (*(uint32_t *)&e32hdr->e_ident[EI_MAG0] !=
|
|
(ELFMAG0 << 24 | ELFMAG1 << 16 | ELFMAG2 << 8 | ELFMAG3) ||
|
|
e32hdr->e_ident[EI_VERSION] != EV_CURRENT)
|
|
return 1; /* Not an ELF */
|
|
|
|
/* file and machine type */
|
|
if (*(uint32_t *)&e32hdr->e_type != (ET_EXEC << 16 | EM_PARISC))
|
|
return 1; /* Not an executable / Wrong architecture */
|
|
|
|
if ((class_data = *(uint16_t *)&e32hdr->e_ident[EI_CLASS]) ==
|
|
(ELFCLASS32 << 8 | ELFDATA2MSB)) {
|
|
|
|
/* support one section executable (ld -N) only */
|
|
if (e32hdr->e_phnum != 1)
|
|
return 1; /* Wrong number of loading sections */
|
|
|
|
/* fill in loading information */
|
|
xi_elf32(inf, e32hdr);
|
|
|
|
} else if (class_data == (ELFCLASS64 << 8 | ELFDATA2MSB)) {
|
|
|
|
/* support one section executable (ld -N) only */
|
|
if (e64hdr->e_phnum != 1)
|
|
return 1; /* Wrong number of loading sections */
|
|
|
|
/* fill in loading information */
|
|
xi_elf64(inf, e64hdr);
|
|
|
|
} else
|
|
return 1; /* Not a 32bit or 64bit ELF */
|
|
|
|
/* move text + data to the top address */
|
|
memmove(buf, inf->sec_image, inf->sec_size);
|
|
|
|
#if 0 /* XXX bss clear is done by the secondary boot itself */
|
|
memset((char *) buf + inf->sec_size, 0, inf->sec_pad);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|