774 lines
17 KiB
C
774 lines
17 KiB
C
|
/* $NetBSD: bootufs.c,v 1.1 1998/09/01 20:02:34 itohy Exp $ */
|
|||
|
|
|||
|
/*-
|
|||
|
* Copyright (c) 1993, 1994 Takumi Nakamura.
|
|||
|
*
|
|||
|
* 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 form must reproduce the above copyright
|
|||
|
* notice, this list of conditions and the following disclaimer in the
|
|||
|
* documentation and/or other materials provided with the distribution.
|
|||
|
* 3. All advertising materials mentioning features or use of this software
|
|||
|
* must display the following acknowledgement:
|
|||
|
* This product includes software developed by Takumi Nakamura.
|
|||
|
* 4. Neither the name of the University nor the names of its contributors
|
|||
|
* may be used to endorse or promote products derived from this software
|
|||
|
* without specific prior written permission.
|
|||
|
*
|
|||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
|||
|
*/
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* file: bootufs.c
|
|||
|
*
|
|||
|
* author: chapuni(GBA02750@niftyserve.or.jp)
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>ͤˤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: UNIX C PROGRAMMING (NUTSHELL)
|
|||
|
* (¯<EFBFBD>˸<EFBFBD><EFBFBD><EFBFBD><EFBFBD>饤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>줫<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˾;
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>̣<EFBFBD><EFBFBD><EFBFBD>ʤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˤ<EFBFBD><EFBFBD>Ƥߤ褦<EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD>ufs <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⥸<EFBFBD>塼<EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD>/sys/lib/libsa <EFBFBD>Τ<EFBFBD><EFBFBD>Τ<EFBFBD>
|
|||
|
* <EFBFBD>Ȥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϥ<EFBFBD><EFBFBD>ʤΤǡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҤȤ⤽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȥ<EFBFBD><EFBFBD>褦<EFBFBD>ˤ<EFBFBD><EFBFBD>褦<EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>褭<EFBFBD>ä<EFBFBD> FD DRIVER <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>夬<EFBFBD><EFBFBD><EFBFBD>ϥ<EFBFBD><EFBFBD>ʤΤǡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD><EFBFBD><EFBFBD>褦<EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
#include <sys/param.h>
|
|||
|
#include <sys/reboot.h>
|
|||
|
#include <sys/types.h>
|
|||
|
#include <ufs/ufs/dinode.h>
|
|||
|
#include <ufs/ffs/fs.h>
|
|||
|
#include <ufs/ufs/dir.h>
|
|||
|
#include <a.out.h>
|
|||
|
#include <machine/bootinfo.h>
|
|||
|
#ifdef SCSI_ADHOC_BOOTPART
|
|||
|
#include <machine/disklabel.h>
|
|||
|
#endif
|
|||
|
|
|||
|
#include "xxboot.h"
|
|||
|
#include "../../x68k/iodevice.h"
|
|||
|
#include "../common/execkern.h"
|
|||
|
#ifdef GZIP
|
|||
|
#include "gunzip/gzip.h"
|
|||
|
#endif
|
|||
|
|
|||
|
/* assertion of sector size == 512 */
|
|||
|
#if !(defined(DEV_BSHIFT) && defined(DEV_BSIZE) && defined(dbtob) \
|
|||
|
&& DEV_BSHIFT == 9 && DEV_BSIZE == 512 && dbtob(1) == 512)
|
|||
|
#error You must make changes to pass sector size to RAW_READ or 64bit-ise it.
|
|||
|
#endif
|
|||
|
|
|||
|
#define alloca __builtin_alloca
|
|||
|
#define memcpy __builtin_memcpy
|
|||
|
|
|||
|
static inline void memset __P((void *p, int v, size_t len));
|
|||
|
#ifdef SCSI_ADHOC_BOOTPART
|
|||
|
static int get_scsi_part __P((void));
|
|||
|
#endif
|
|||
|
static int get_scsi_host_adapter __P((void));
|
|||
|
|
|||
|
static inline int
|
|||
|
strcmp(a, b)
|
|||
|
const char *a;
|
|||
|
const char *b;
|
|||
|
{
|
|||
|
|
|||
|
while (*a && *a == *b)
|
|||
|
a++, b++;
|
|||
|
return (*a != *b);
|
|||
|
}
|
|||
|
|
|||
|
static inline int
|
|||
|
memcmp(a, b, n)
|
|||
|
const char *a;
|
|||
|
const char *b;
|
|||
|
int n;
|
|||
|
{
|
|||
|
|
|||
|
while (*a == *b && --n)
|
|||
|
a++, b++;
|
|||
|
return (*a != *b);
|
|||
|
}
|
|||
|
|
|||
|
static inline void
|
|||
|
memset(p, v, len)
|
|||
|
void *p;
|
|||
|
int v;
|
|||
|
size_t len;
|
|||
|
{
|
|||
|
|
|||
|
while (len--)
|
|||
|
*(char *)p++ = v;
|
|||
|
}
|
|||
|
|
|||
|
/* for debug */
|
|||
|
#ifdef DEBUG_WITH_STDIO
|
|||
|
extern int printf __P((const char *, ...));
|
|||
|
#endif
|
|||
|
|
|||
|
#define IODEVbase ((volatile struct IODEVICE *)PHYS_IODEV)
|
|||
|
|
|||
|
union {
|
|||
|
struct fs superblk; /* <20>ۥ<EFBFBD><DBA5><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
unsigned char buf[SBSIZE]; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>碌 */
|
|||
|
} superblk_buf;
|
|||
|
|
|||
|
#define sblock superblk_buf.superblk
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD>ǥ<EFBFBD><EFBFBD>쥯<EFBFBD>ȥꥨ<EFBFBD><EFBFBD><EFBFBD>ȥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Хåե<EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
#define N_VMUNIX 24
|
|||
|
|
|||
|
struct {
|
|||
|
ino_t ino; /* inode */
|
|||
|
char name[60];
|
|||
|
} vmunix_dirent[N_VMUNIX];
|
|||
|
|
|||
|
char *lowram;
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD>֥<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD><EFBFBD>Ϣ³<EFBFBD>ɤߤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߤ롣
|
|||
|
* length = 0 <EFBFBD>ΤȤ<EFBFBD><EFBFBD>ϡ<EFBFBD>ñ<EFBFBD>˥Хåե<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ե<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD>夹<EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȥʤ롣
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
void *s_buf;
|
|||
|
u_int32_t s_blkpos;
|
|||
|
size_t s_len;
|
|||
|
|
|||
|
int
|
|||
|
raw_read_queue(buf, blkpos, len)
|
|||
|
void *buf;
|
|||
|
u_int32_t blkpos;
|
|||
|
size_t len;
|
|||
|
{
|
|||
|
int r = 0;
|
|||
|
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2>⤷<EFBFBD>ʤ<EFBFBD> */
|
|||
|
if (s_len == 0) {
|
|||
|
s_buf = buf;
|
|||
|
s_blkpos = blkpos;
|
|||
|
s_len = len;
|
|||
|
return r;
|
|||
|
}
|
|||
|
/* <20><><EFBFBD>Υ<EFBFBD><CEA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ³<CFA2><C2B3><EFBFBD>Ƥ<EFBFBD><C6A4><EFBFBD><EFBFBD>Ȥ<EFBFBD> */
|
|||
|
if (len > 0 && s_blkpos + btodb(s_len) == blkpos) {
|
|||
|
s_len += len;
|
|||
|
return r;
|
|||
|
}
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD>ޤ<EFBFBD>ί<EFBFBD>äƤ<C3A4><C6A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Τ<EFBFBD><CEA4>ɤ<EFBFBD> */
|
|||
|
r = RAW_READ(s_buf, s_blkpos, s_len);
|
|||
|
s_buf = buf;
|
|||
|
s_blkpos = blkpos;
|
|||
|
s_len = len;
|
|||
|
return r;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><EFBFBD>֥<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤߤ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
get_superblk()
|
|||
|
{
|
|||
|
|
|||
|
RAW_READ(&superblk_buf.buf, SBLOCK, SBSIZE);
|
|||
|
#ifdef DEBUG_WITH_STDIO
|
|||
|
printf("fs_magic=%08lx\n", sblock.fs_magic);
|
|||
|
printf("fs_ipg=%08lx\n", sblock.fs_ipg);
|
|||
|
printf("fs_ncg=%08lx\n", sblock.fs_ncg);
|
|||
|
printf("fs_bsize=%08lx\n", sblock.fs_bsize);
|
|||
|
printf("fs_fsize=%08lx\n", sblock.fs_fsize);
|
|||
|
printf("INOPB=%08lx\n", INOPB(&sblock));
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* inode <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>äƤ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
get_inode(ino, pino)
|
|||
|
ino_t ino;
|
|||
|
struct dinode *pino;
|
|||
|
{
|
|||
|
struct dinode *buf = alloca((size_t) sblock.fs_bsize);
|
|||
|
|
|||
|
RAW_READ(buf,
|
|||
|
fsbtodb(&sblock, (u_int32_t) ino_to_fsba(&sblock, ino)),
|
|||
|
(size_t) sblock.fs_bsize);
|
|||
|
*pino = buf[ino_to_fsbo(&sblock, ino)];
|
|||
|
#ifdef DEBUG_WITH_STDIO
|
|||
|
printf("%d)permission=%06ho\n", ino, pino->di_mode);
|
|||
|
printf("%d)nlink=%4d\n", ino, pino->di_nlink);
|
|||
|
printf("%d)uid=%4d\n", ino, pino->di_uid);
|
|||
|
printf("%d)gid=%4d\n", ino, pino->di_gid);
|
|||
|
printf("%d)size=%ld\n", ino, pino->di_size);
|
|||
|
#endif
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* inode <EFBFBD>λؤ<EFBFBD><EFBFBD>Ƥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>֥<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>äƤ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
* NutShell <EFBFBD>饤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Υѥ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
* buf <EFBFBD>ˤϥǡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤ<EFBFBD><EFBFBD>Ǥ<EFBFBD><EFBFBD>륢<EFBFBD>ɥ쥹<EFBFBD>åȤ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƥۤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Х֥<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڤꤢ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
read_indirect(blkno, level, buf, count)
|
|||
|
ufs_daddr_t blkno;
|
|||
|
int level;
|
|||
|
void **buf;
|
|||
|
int count;
|
|||
|
{
|
|||
|
ufs_daddr_t idblk[MAXBSIZE / sizeof(ufs_daddr_t)];
|
|||
|
int i;
|
|||
|
|
|||
|
RAW_READ(idblk,
|
|||
|
fsbtodb(&sblock, (u_int32_t) blkno),
|
|||
|
(size_t) sblock.fs_bsize);
|
|||
|
|
|||
|
for (i = 0; i < NINDIR(&sblock) && count > 0; i++) {
|
|||
|
if (level) {
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD>˴<EFBFBD><CBB4>ܥ֥<DCA5><D6A5>å<EFBFBD><C3A5><EFBFBD><EFBFBD>ɤޤ<C9A4><DEA4><EFBFBD> */
|
|||
|
count = read_indirect(idblk[i], level - 1, buf, count);
|
|||
|
} else {
|
|||
|
/* <20>ǡ<EFBFBD><C7A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤ<EFBFBD> */
|
|||
|
#ifdef DEBUG_WITH_STDIO
|
|||
|
printf("%d%4d\n", level, idblk[i]);
|
|||
|
#endif
|
|||
|
raw_read_queue(*buf,
|
|||
|
fsbtodb(&sblock, (u_int32_t) idblk[i]),
|
|||
|
(size_t) sblock.fs_bsize);
|
|||
|
*buf += sblock.fs_bsize;
|
|||
|
count -= sblock.fs_bsize;
|
|||
|
}
|
|||
|
}
|
|||
|
return count;
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
read_blocks(dp, buf, count)
|
|||
|
struct dinode *dp;
|
|||
|
void *buf;
|
|||
|
int count;
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
if (dp->di_size < (unsigned) count)
|
|||
|
count = dp->di_size;
|
|||
|
|
|||
|
s_len = 0;
|
|||
|
|
|||
|
/* direct block <20><><EFBFBD>ɤ<EFBFBD> */
|
|||
|
for (i = 0; i < NDADDR && count > 0; i++) {
|
|||
|
#ifdef DEBUG_WITH_STDIO
|
|||
|
printf(" %4d\n", dp->di_db[i]);
|
|||
|
#endif
|
|||
|
raw_read_queue(buf,
|
|||
|
fsbtodb(&sblock, (u_int32_t) dp->di_db[i]),
|
|||
|
(size_t) sblock.fs_bsize);
|
|||
|
buf += sblock.fs_bsize;
|
|||
|
count -= sblock.fs_bsize;
|
|||
|
}
|
|||
|
|
|||
|
/* indirect block <20><><EFBFBD>ɤ<EFBFBD> */
|
|||
|
for (i = 0; i < NIADDR && count > 0; i++) {
|
|||
|
count = read_indirect(dp->di_ib[i], i, &buf, count);
|
|||
|
}
|
|||
|
/* <20>Хåե<C3A5><D5A5>Υե<CEA5><D5A5>å<EFBFBD><C3A5><EFBFBD> */
|
|||
|
raw_read_queue(NULL, (u_int32_t) 0, (size_t) 0);
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>ꤵ<EFBFBD>줿<EFBFBD>ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> inode <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
ino_t
|
|||
|
search_file(dirino, filename)
|
|||
|
ino_t dirino; /* <20>ǥ<EFBFBD><C7A5>쥯<EFBFBD>ȥ<EFBFBD><C8A5>ΰ<EFBFBD><CEB0><EFBFBD> */
|
|||
|
const char *filename; /* <20>ե<EFBFBD><D5A5><EFBFBD><EFBFBD><EFBFBD>̾ */
|
|||
|
{
|
|||
|
void *dirp;
|
|||
|
struct dinode dinode;
|
|||
|
struct direct *dir;
|
|||
|
|
|||
|
get_inode(dirino, &dinode);
|
|||
|
dirp = alloca((size_t) (dinode.di_size + MAXBSIZE - 1) & ~((unsigned) MAXBSIZE - 1));
|
|||
|
read_blocks(&dinode, dirp, (int) dinode.di_size);
|
|||
|
|
|||
|
while (dir = dirp, dir->d_ino != 0) {
|
|||
|
if (!strcmp(dir->d_name, filename))
|
|||
|
return dir->d_ino;
|
|||
|
dirp += dir->d_reclen;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>ꤵ<EFBFBD>줿<EFBFBD>ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤ߹<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǥ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
unsigned
|
|||
|
load_ino(buf, ino, filename)
|
|||
|
void *buf;
|
|||
|
ino_t ino;
|
|||
|
const char *filename;
|
|||
|
{
|
|||
|
struct dinode dinode;
|
|||
|
|
|||
|
B_PRINT("loading ");
|
|||
|
B_PRINT(filename);
|
|||
|
B_PRINT("...");
|
|||
|
get_inode(ino, &dinode);
|
|||
|
read_blocks(&dinode, buf, (int) dinode.di_size);
|
|||
|
return dinode.di_size;
|
|||
|
}
|
|||
|
|
|||
|
unsigned
|
|||
|
load_name(buf, dirino, filename)
|
|||
|
void *buf; /* <20>ɤ߹<C9A4><DFB9><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
ino_t dirino; /* <20><><EFBFBD><EFBFBD><EFBFBD>ǥ<EFBFBD><C7A5>쥯<EFBFBD>ȥ<EFBFBD><C8A5><EFBFBD> inode <20>ֹ<EFBFBD> */
|
|||
|
const char *filename; /* <20>ե<EFBFBD><D5A5><EFBFBD><EFBFBD><EFBFBD>̾ */
|
|||
|
{
|
|||
|
ino_t ino;
|
|||
|
|
|||
|
ino = search_file(dirino, filename);
|
|||
|
return ino ? load_ino(buf, ino, filename) : 0;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* for test
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>ΤȤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>vmunix <EFBFBD>ϥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܥ<EFBFBD><EFBFBD>å<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>ʤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ū<EFBFBD>ˤϤɤ<EFBFBD><EFBFBD>ˤ<EFBFBD><EFBFBD>ʤ뤫<EFBFBD>⤷<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
print_hex(x, l)
|
|||
|
unsigned x; /* ɽ<><C9BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
int l; /* ɽ<><C9BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
{
|
|||
|
|
|||
|
if (l > 0) {
|
|||
|
print_hex(x >> 4, l - 1);
|
|||
|
x &= 0x0F;
|
|||
|
if (x > 9)
|
|||
|
x += 7;
|
|||
|
B_PUTC('0' + x);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* vmunix.* netbsd.* <EFBFBD>Υꥹ<EFBFBD>Ȥ<EFBFBD><EFBFBD>Ĥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ф<EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
pickup_list(dirino)
|
|||
|
ino_t dirino;
|
|||
|
{
|
|||
|
void *dirp;
|
|||
|
struct dinode dinode;
|
|||
|
struct direct *dir;
|
|||
|
int n = 0;
|
|||
|
|
|||
|
get_inode(dirino, &dinode);
|
|||
|
dirp = alloca((size_t) (dinode.di_size + MAXBSIZE - 1) & ~((unsigned) MAXBSIZE - 1));
|
|||
|
read_blocks(&dinode, dirp, (int) dinode.di_size);
|
|||
|
|
|||
|
while (dir = dirp, dir->d_ino != 0) {
|
|||
|
if (!memcmp(dir->d_name, "vmunix", 6)
|
|||
|
|| !memcmp(dir->d_name, "netbsd", 6)) {
|
|||
|
vmunix_dirent[n].ino = dir->d_ino;
|
|||
|
memcpy(vmunix_dirent[n].name, dir->d_name, 60);
|
|||
|
if (++n >= N_VMUNIX)
|
|||
|
return;
|
|||
|
}
|
|||
|
dirp += dir->d_reclen;
|
|||
|
}
|
|||
|
while (n < N_VMUNIX)
|
|||
|
vmunix_dirent[n++].ino = 0;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* vmunix_dirent[] <EFBFBD>Υ쥳<EFBFBD><EFBFBD><EFBFBD>ɤ<EFBFBD>ɽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
print_list(n, active, boothowto)
|
|||
|
int n;
|
|||
|
int active;
|
|||
|
unsigned boothowto;
|
|||
|
{
|
|||
|
|
|||
|
if (!vmunix_dirent[n].ino)
|
|||
|
return;
|
|||
|
B_LOCATE(0, 7 + n);
|
|||
|
B_PRINT(active && (boothowto & RB_SINGLE)
|
|||
|
? "SINGLE "
|
|||
|
: " ");
|
|||
|
print_hex(vmunix_dirent[n].ino, 8);
|
|||
|
B_PRINT(" ");
|
|||
|
if (active)
|
|||
|
B_COLOR(0x0F);
|
|||
|
B_PRINT(vmunix_dirent[n].name);
|
|||
|
B_COLOR(0x03);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef SCSI_ADHOC_BOOTPART
|
|||
|
/*
|
|||
|
* get partition # from partition start position
|
|||
|
*/
|
|||
|
|
|||
|
#define NPART 15
|
|||
|
#define PARTTBL_TOP ((unsigned)4) /* pos of part inf in 512byte-blocks */
|
|||
|
#define MAXPART 6
|
|||
|
const unsigned char partition_conv[MAXPART + 1] = { 0, 1, 3, 4, 5, 6, 7 };
|
|||
|
|
|||
|
static int
|
|||
|
get_scsi_part()
|
|||
|
{
|
|||
|
struct {
|
|||
|
u_int32_t magic; /* 0x5836384B ("X68K") */
|
|||
|
u_int32_t parttotal;
|
|||
|
u_int32_t diskblocks;
|
|||
|
u_int32_t diskblocks2; /* backup? */
|
|||
|
struct dos_partition parttbl[NPART];
|
|||
|
unsigned char formatstr[256];
|
|||
|
unsigned char rest[512];
|
|||
|
} partbuf;
|
|||
|
int i;
|
|||
|
u_int32_t part_top;
|
|||
|
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("seclen: ");
|
|||
|
print_hex(SCSI_BLKLEN, 8); /* 0: 256, 1: 512, 2: 1024 */
|
|||
|
B_PRINT(", topsec: ");
|
|||
|
print_hex(SCSI_PARTTOP, 8); /* partition top in sector */
|
|||
|
#endif
|
|||
|
/*
|
|||
|
* read partition table
|
|||
|
*/
|
|||
|
RAW_READ0(&partbuf, PARTTBL_TOP, sizeof partbuf);
|
|||
|
|
|||
|
part_top = SCSI_PARTTOP >> (2 - SCSI_BLKLEN);
|
|||
|
for (i = 0; i < MAXPART; i++)
|
|||
|
if ((u_int32_t) partbuf.parttbl[i].dp_start == part_top)
|
|||
|
goto found;
|
|||
|
|
|||
|
BOOT_ERROR("Can't boot from this partition");
|
|||
|
/* NOTREACHED */
|
|||
|
found:
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("; sd");
|
|||
|
B_PUTC((unsigned)ID + '0'); /* SCSI ID (not NetBSD unit #) */
|
|||
|
B_PUTC((unsigned)partition_conv[i] + 'a');
|
|||
|
#endif
|
|||
|
return partition_conv[i];
|
|||
|
}
|
|||
|
#endif /* SCSI_ADHOC_BOOTPART */
|
|||
|
|
|||
|
/*
|
|||
|
* Check the type of SCSI interface
|
|||
|
*/
|
|||
|
static int
|
|||
|
get_scsi_host_adapter()
|
|||
|
{
|
|||
|
char *bootrom;
|
|||
|
int ha;
|
|||
|
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT(" at ");
|
|||
|
#endif
|
|||
|
bootrom = (char *) (BOOT_INFO & 0x00ffffe0);
|
|||
|
if (!memcmp(bootrom + 0x24, "SCSIIN", 6)) {
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("spc0");
|
|||
|
#endif
|
|||
|
ha = (X68K_BOOT_SCSIIF_SPC << 4) | 0;
|
|||
|
} else if (badbaddr(&IODEVbase->io_exspc.bdid)) {
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("mha0");
|
|||
|
#endif
|
|||
|
ha = (X68K_BOOT_SCSIIF_MHA << 4) | 0;
|
|||
|
} else {
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("spc1");
|
|||
|
#endif
|
|||
|
ha = (X68K_BOOT_SCSIIF_SPC << 4) | 1;
|
|||
|
}
|
|||
|
|
|||
|
return ha;
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
*
|
|||
|
* <EFBFBD>ޤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>衩
|
|||
|
*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD>٤Ƥ<EFBFBD> IPL <EFBFBD>⥸<EFBFBD>塼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɤ߹<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȥϡ<EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǥ<EFBFBD><EFBFBD>롣<EFBFBD>֥<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ž<EFBFBD><EFBFBD><EFBFBD>פ<EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ޤǤϡ<EFBFBD>
|
|||
|
* x68k IOCS <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƥ<EFBFBD><EFBFBD>ޤ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߤ<EFBFBD><EFBFBD>ޥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ޤǤ<EFBFBD>
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>פǤ<EFBFBD><EFBFBD>礦<EFBFBD><EFBFBD>
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
volatile void
|
|||
|
bootufs()
|
|||
|
{
|
|||
|
unsigned long boothowto;
|
|||
|
unsigned long esym;
|
|||
|
int i;
|
|||
|
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0x100000 <20><><EFBFBD><EFBFBD>¸<EFBFBD>ߤ<EFBFBD><DFA4>Ƥ<EFBFBD><C6A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
extern struct exec header;
|
|||
|
extern char image[];
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD>ϥ<EFBFBD><CFA5>ԡ<EFBFBD><D4A1><EFBFBD> */
|
|||
|
struct execkern_arg execinfo;
|
|||
|
|
|||
|
int bootdev;
|
|||
|
unsigned short SFT;
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
/* for debug; <20><>ư<EFBFBD><C6B0><EFBFBD>Υ쥸<CEA5><ECA5B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>äƤ<C3A4><C6A4><EFBFBD> */
|
|||
|
extern unsigned startregs[16];
|
|||
|
#endif
|
|||
|
|
|||
|
/* MPU <20><><EFBFBD><EFBFBD><EFBFBD>å<EFBFBD> */
|
|||
|
#if 0
|
|||
|
int sys_stat;
|
|||
|
|
|||
|
sys_stat = SYS_STAT(0);
|
|||
|
if ((sys_stat & 255) == 0 ||
|
|||
|
(getcpu() != 4 && !(sys_stat & (1 << 14)))) {
|
|||
|
BOOT_ERROR("MMU <20><><EFBFBD>ʤ<EFBFBD><CAA4><EFBFBD><EFBFBD>ᡢNetBSD <20><><EFBFBD><EFBFBD>ư<EFBFBD>Ǥ<EFBFBD><C7A4>ޤ<EFBFBD><DEA4><EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
/* NOTREACHED */
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
/* for debug; <20>쥸<EFBFBD><ECA5B8><EFBFBD><EFBFBD><EFBFBD>ξ<EFBFBD><CEBE>֤<EFBFBD><D6A4>ץ<EFBFBD><D7A5><EFBFBD><EFBFBD>Ȥ<EFBFBD><C8A4><EFBFBD> */
|
|||
|
for (i = 0; i < 16; i++) {
|
|||
|
print_hex(startregs[i], 8);
|
|||
|
B_PRINT((i & 7) == 7 ? "\r\n" : " ");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* get boot device
|
|||
|
*/
|
|||
|
if (BINF_ISFD(&BOOT_INFO)) {
|
|||
|
/* floppy */
|
|||
|
bootdev = X68K_MAKEBOOTDEV(X68K_MAJOR_FD, BOOT_INFO & 3,
|
|||
|
(FDSECMINMAX.minsec.N == 3) ? 0 : 2);
|
|||
|
} else {
|
|||
|
/* SCSI */
|
|||
|
int part, ha;
|
|||
|
|
|||
|
#ifdef SCSI_ADHOC_BOOTPART
|
|||
|
part = get_scsi_part();
|
|||
|
#else
|
|||
|
part = 0; /* sd?a only */
|
|||
|
#endif
|
|||
|
ha = get_scsi_host_adapter();
|
|||
|
bootdev = X68K_MAKESCSIBOOTDEV(X68K_MAJOR_SD, ha >> 4, ha & 15,
|
|||
|
ID & 7, 0, part);
|
|||
|
}
|
|||
|
#ifdef BOOT_DEBUG
|
|||
|
B_PRINT("\r\nbootdev: ");
|
|||
|
print_hex((unsigned) bootdev, 8);
|
|||
|
B_PRINT("\r\nhit key\r\n");
|
|||
|
(void) B_KEYINP(); /* wait key input */
|
|||
|
#endif
|
|||
|
|
|||
|
/* boothowto <20>å<F2A5BBA5> */
|
|||
|
boothowto = RB_AUTOBOOT;
|
|||
|
|
|||
|
/* <20><><EFBFBD>եȥ<D5A5><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƥ<EFBFBD><C6A4><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
SFT = B_SFTSNS();
|
|||
|
if (SFT & 0x01)
|
|||
|
boothowto |= RB_SINGLE;
|
|||
|
|
|||
|
/* vmunix <20><><EFBFBD>ɤ߹<C9A4><DFB9><EFBFBD><EFBFBD>Ǥ<EFBFBD><C7A4><EFBFBD> */
|
|||
|
get_superblk();
|
|||
|
if (sblock.fs_magic != FS_MAGIC) {
|
|||
|
BOOT_ERROR("bogus super block: "
|
|||
|
"<EFBFBD>롼<EFBFBD>ȥե<EFBFBD><EFBFBD><EFBFBD><EFBFBD>륷<EFBFBD><EFBFBD><EFBFBD>ƥब<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƥ<EFBFBD><EFBFBD>ޤ<EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
/* NOTREACHED */
|
|||
|
}
|
|||
|
|
|||
|
if (boothowto == RB_AUTOBOOT) {
|
|||
|
esym = load_name(&header, ROOTINO, "netbsd");
|
|||
|
#ifdef GZIP
|
|||
|
if (!esym)
|
|||
|
esym = load_name(&header, ROOTINO, "netbsd.gz");
|
|||
|
#endif
|
|||
|
|
|||
|
SFT = B_SFTSNS();
|
|||
|
if (SFT & 0x0001)
|
|||
|
boothowto |= RB_SINGLE;
|
|||
|
}
|
|||
|
|
|||
|
if (boothowto != RB_AUTOBOOT || !esym) {
|
|||
|
int x = 0;
|
|||
|
|
|||
|
printtitle();
|
|||
|
B_PRINT("....<2E>ɤΥ<C9A4><CEA5><EFBFBD><EFBFBD>ͥ<EFBFBD><CDA5><EFBFBD><EFBFBD>ɤ߹<C9A4><DFB9>ߤޤ<DFA4><DEA4><EFBFBD><EFBFBD><EFBFBD>\r\n"
|
|||
|
"SHIFT <20><> RB_SINGLE <20><><EFBFBD>ȥ<EFBFBD><C8A5>뤷<EFBFBD>ޤ<EFBFBD><DEA4><EFBFBD>\r\n");
|
|||
|
|
|||
|
/* <20>롼<EFBFBD>ȥǥ<C8A5><C7A5>쥯<EFBFBD>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϥ<EFBFBD><CFA4><EFBFBD> */
|
|||
|
pickup_list(ROOTINO);
|
|||
|
|
|||
|
/* ɽ<><C9BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
for (i = 0; i < N_VMUNIX; i++) {
|
|||
|
if (!memcmp(vmunix_dirent[i].name, "netbsd", 7)) {
|
|||
|
print_list(x, 0, boothowto);
|
|||
|
x = i;
|
|||
|
}
|
|||
|
print_list(i, i == x, boothowto);
|
|||
|
}
|
|||
|
|
|||
|
wait_shift_release:
|
|||
|
while (B_SFTSNS() & 0x0001)
|
|||
|
;
|
|||
|
|
|||
|
/* <20><><EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD><D4A4>롼<EFBFBD>פ<EFBFBD><D7A4><EFBFBD> */
|
|||
|
for (;;) {
|
|||
|
switch ((B_KEYINP() >> 8) & 0xFF) {
|
|||
|
case 0x3C: /* UP */
|
|||
|
if (x > 0) {
|
|||
|
print_list(--x, 1, boothowto);
|
|||
|
print_list(x + 1, 0, boothowto);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 0x3E: /* DOWN */
|
|||
|
if (x < N_VMUNIX - 1
|
|||
|
&& vmunix_dirent[x + 1].ino) {
|
|||
|
print_list(++x, 1, boothowto);
|
|||
|
print_list(x - 1, 0, boothowto);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 0x70: /* SHIFT */
|
|||
|
boothowto ^= RB_SINGLE;
|
|||
|
print_list(x, 1, boothowto);
|
|||
|
goto wait_shift_release;
|
|||
|
case 0x1D: /* CR */
|
|||
|
case 0x4E: /* ENTER */
|
|||
|
goto exit_loop;
|
|||
|
}
|
|||
|
}
|
|||
|
exit_loop:
|
|||
|
printtitle();
|
|||
|
esym = load_ino(&header,
|
|||
|
vmunix_dirent[x].ino, vmunix_dirent[x].name);
|
|||
|
}
|
|||
|
#ifdef GZIP
|
|||
|
load_done:
|
|||
|
#endif
|
|||
|
B_PRINT("done.\r\n");
|
|||
|
|
|||
|
#ifdef GZIP
|
|||
|
/*
|
|||
|
* Check if the file is gzip'ed
|
|||
|
*/
|
|||
|
if (check_gzip_magic((char *) &header)) {
|
|||
|
/*
|
|||
|
* uncompress
|
|||
|
*/
|
|||
|
char *ctop; /* gzip'ed top */
|
|||
|
|
|||
|
/*
|
|||
|
* transfer loaded file to safe? area
|
|||
|
*
|
|||
|
* 0x3F0000 - SIZE_ALLOC_BUF - ?
|
|||
|
* (stack bottom) (see inflate.c)
|
|||
|
*/
|
|||
|
ctop = (char *) 0x3e8000 - esym;
|
|||
|
memcpy(ctop, &header, esym);
|
|||
|
|
|||
|
B_PRINT("decompressing...");
|
|||
|
|
|||
|
/* unzip() doesn't return on failure */
|
|||
|
esym = unzip(ctop, (char *) &header);
|
|||
|
|
|||
|
goto load_done;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/* <20>֤Ҥ餬<D2A4>ʡפǡ<D7A4>ddb <20><EFBFBD><F2B5AFA4><EFBFBD> */
|
|||
|
SFT = B_SFTSNS();
|
|||
|
if (SFT & 0x2000)
|
|||
|
boothowto |= RB_KDB;
|
|||
|
|
|||
|
/* <20>إå<D8A5><C3A5>ԡ<F2A5B3A5><D4A1><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
execinfo.image_top = image;
|
|||
|
execinfo.load_addr = 0;
|
|||
|
execinfo.text_size = header.a_text;
|
|||
|
execinfo.data_size = header.a_data;
|
|||
|
execinfo.bss_size = header.a_bss;
|
|||
|
execinfo.symbol_size = header.a_syms;
|
|||
|
execinfo.d5 = BOOT_INFO; /* unused for now */
|
|||
|
execinfo.rootdev = bootdev;
|
|||
|
execinfo.boothowto = boothowto;
|
|||
|
execinfo.entry_addr = header.a_entry;
|
|||
|
|
|||
|
/* <20><><EFBFBD>Τ<CEA4><F2A5B3A5><EFBFBD> load <20><><EFBFBD><EFBFBD> */
|
|||
|
if (N_GETMID(header) == MID_M68K
|
|||
|
&& N_GETMAGIC(header) == NMAGIC) {
|
|||
|
|
|||
|
/*
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶ػߤǤ<EFBFBD><EFBFBD>롣
|
|||
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĥ<EFBFBD><EFBFBD>Ƥ<EFBFBD><EFBFBD>졣
|
|||
|
*/
|
|||
|
asm("oriw #0x0700,sr");
|
|||
|
|
|||
|
/* <20>¹<EFBFBD> */
|
|||
|
exec_kernel(&execinfo);
|
|||
|
/* NOTREACHED */
|
|||
|
} else {
|
|||
|
BOOT_ERROR("improper file format: <20>¹<EFBFBD><C2B9>Բ<EFBFBD>ǽ<EFBFBD>Ǥ<EFBFBD><C7A4><EFBFBD>");
|
|||
|
/* NOTREACHED */
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* eof */
|