root/sys/arch/hpcmips/stand/lcboot/main.c
/* $NetBSD: main.c,v 1.8 2026/06/11 19:46:18 andvar Exp $ */

/*
 * Copyright (c) 2003 Naoto Shimazaki.
 * 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 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.
 *
 * THIS SOFTWARE IS PROVIDED BY NAOTO SHIMAZAKI 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 NAOTO 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.
 */

/*
 * Boot loader for L-Card+
 *
 * ROM Map
 * -------
 * ROM1
 * BFFF FFFF    ------------------------------
 *
 *              reserved
 *
 * BF80 0000    ------------------------------
 *
 * ROM0
 * BFFF FFFF    ------------------------------
 *
 *              user storage (max 2Mbytes)
 *
 * BFE0 0000    ------------------------------
 *
 *              reserved
 *
 * BFD4 0000    ------------------------------
 *
 *              boot params
 *
 * BFD2 0000    ------------------------------
 *
 *              second boot loader (mirror image)
 *              or Linux Kernel
 *
 * BFD0 0000    ------------------------------
 *
 *              first boot loader (L-Card+ original loader)
 *
 *              reset vector
 * BFC0 0000    ------------------------------
 *
 *              gziped kernel image (max 4Mbytes)
 *
 * BF80 0000    ------------------------------
 *
 *
 *
 * RAM Map
 * -------
 *
 * 80FF FFFF    ------------------------------
 *              ROM ICE work
 * 80FF FE00    ------------------------------
 *              ROM ICE stack
 * 80FF FDA8    ------------------------------
 *
 *
 *
 *              kernel
 * 8004 0000    ------------------------------
 *              kernel stack (growing to lower)
 *
 *
 *              boot loader heap (growing to upper)
 *              boot loader text & data (at exec time)
 * 8000 1000    ------------------------------
 *              vector table
 * 8000 0000    ------------------------------
 *
 *              virtual memory space
 *
 * 0000 0000    ------------------------------
 *
 *
 *
 * ROMCS0 <-> ROMCS3 mapping
 *
 *  ROMCS0        ROMCS3
 * BE7F FFFF <-> BFFF FFFF
 * BE40 0000 <-> BFC0 0000      reset vector
 * BE00 0000 <-> BF80 0000
 * 
 *
 */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: main.c,v 1.8 2026/06/11 19:46:18 andvar Exp $");

#include <lib/libsa/stand.h>

#include <lib/libsa/loadfile.h>
#include <lib/libkern/libkern.h>

#include <hpcmips/vr/vripreg.h>
#include <hpcmips/vr/cmureg.h>
#include <hpcmips/vr/vr4181giureg.h>

#include "extern.h"
#include "i28f128reg.h"

/* XXX */
#define ISABRGCTL       0x00
#define ISABRGSTS       0x02
#define XISACTL         0x04

#define BOOTTIMEOUT     9       /* must less than 10 */
#define LINEBUFLEN      80

extern const char bootprog_rev[];
extern const char bootprog_name[];

static void command_help(char *opt);
static void command_dump(char *opt);
static void command_boot(char *opt);
static void command_load(char *opt);
static void command_fill(char *opt);
static void command_write(char *opt);
static void command_option(char *subcmd);
static void opt_subcmd_print(char *opt);
static void opt_subcmd_read(char *opt);
static void opt_subcmd_write(char *opt);
static void opt_subcmd_path(char *opt);
static void opt_subcmd_bootp(char *opt);
static void opt_subcmd_ip(char *opt);


struct boot_option      bootopts;

static struct bootmenu_command commands[] = {
        { "?",          command_help },
        { "h",          command_help },
        { "d",          command_dump },
        { "b",          command_boot },
        { "l",          command_load },
        { "f",          command_fill },
        { "w",          command_write },
        { "o",          command_option },
        { NULL,         NULL },
};

static struct bootmenu_command opt_subcommands[] = {
        { "p",          opt_subcmd_print },
        { "r",          opt_subcmd_read },
        { "w",          opt_subcmd_write },
        { "path",       opt_subcmd_path },
        { "bootp",      opt_subcmd_bootp },
        { "ip",         opt_subcmd_ip },
        { NULL,         NULL },
};

static void
print_banner(void)
{
        printf("\n");
        printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
#if 0
        printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
#endif
}

static void
init_devices(void)
{
        /* Init RTC */
        REGWRITE_2(VRETIMEH, 0, 0);
        REGWRITE_2(VRETIMEM, 0, 0);
        REGWRITE_2(VRETIMEL, 0, 0);


        /*
         * CLKSPEEDREG  0x6012
         *      DIV     DIV2 mode
         *      CLKSP   18 (0x12)
         *      PClock (CPU clock)              65.536MHz
         *              PClock = (18.432MHz / CLKSP) x 64
         *                     = (18.432MHz / 18) x 64
         *                     = 65.536MHz
         *      TClock (peripheral clock)       32.768MHz
         *              TClock = PClock / DIV
         *                     = 65.536MHz / 2
         *                     = 32.768MHz
         */

        /*
         * setup ISA BUS clock frequency
         *
         * set PCLK (internal peripheral clock) to 32.768MHz (TClock / 1)
         * set External ISA bus clock to 10.922MHz (TClock / 3)
         */
        REGWRITE_2(VR4181_ISABRG_ADDR, ISABRGCTL, 0x0003);
        REGWRITE_2(VR4181_ISABRG_ADDR, XISACTL, 0x0401);

        /*
         * setup peripheral's clock supply
         *
         * CSU: disable
         * AIU: enable (AIU, ADU, ADU18M)
         * PIU: disable
         * SIU: enable (SIU18M)
         */
        REGWRITE_2(VR4181_CMU_ADDR, 0, CMUMASK_SIU | CMUMASK_AIU);

        /*
         * setup GPIO
         */
#if 0
        /* L-Card+ generic setup */
        /*
         * pin   mode   comment
         * GP0 : GPI    not used
         * GP1 : GPI    not used
         * GP2 : GPO    LED6 (0: on  1: off)
         * GP3 : PCS0   chip select for CS8900A Lan controller
         * GP4 : GPI    IRQ input from CS8900A
         * GP5 : GPI    not used
         * GP6 : GPI    not used
         * GP7 : GPI    reserved by TANBAC TB0193
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
                   GP3_PCS0 | GP2_GPO);
        /*
         * pin   mode   comment
         * GP8 : GPO    LED5 (0: on  1: off)
         * GP9 : GPI    CD2
         * GP10: GPI    CD1
         * GP11: GPI    not used
         * GP12: GPI    not used
         * GP13: GPI    not used
         * GP14: GPI    not used
         * GP15: GPI    not used
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W, GP8_GPO);
        /*
         * pin   mode   comment
         * GP16: IORD   ISA bus
         * GP17: IOWR   ISA bus
         * GP18: IORDY  ISA bus
         * GP19: GPI    not used
         * GP20: GPI    not used
         * GP21: RESET  resets CS8900A
         * GP22: ROMCS0 ROM chip select
         * GP23: ROMCS1 ROM chip select
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
                   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
                   | GP18_IORDY | GP17_IOWR | GP16_IORD);
        /*
         * GP24: ROMCS2 ROM chip select
         * GP25: RxD1   SIU1
         * GP26: TxD1   SIU1
         * GP27: RTS1   SIU1
         * GP28: CTS1   SIU1
         * GP29: GPI    LED3
         * GP30: GPI    reserved by TANBAC TB0193
         * GP31: GPI    LED4
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
                   GP30_GPI
                   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
                   | GP24_ROMCS2);
#else
        /* e-care node specific setup */
        /*
         * pin   mode   comment
         * GP0 : GPO    ECNRTC_RST
         * GP1 : GPO    ECNRTC_CLK
         * GP2 : GPO    LED6 (0: on  1: off)
         * GP3 : PCS0   chip select for CS8900A Lan controller
         * GP4 : GPI    IRQ input from CS8900A
         * GP5 : GPO    ECNRTC_DIR
         * GP6 : GPO    ECNRTC_OUT
         * GP7 : GPI    reserved by TANBAC TB0193
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
                   GP6_GPO | GP5_GPO | GP3_PCS0
                   | GP2_GPO | GP1_GPO | GP0_GPO);

        /*
         * pin   mode   comment
         * GP8 : GPO    LED5 (0: on  1: off)
         * GP9 : GPI    CD2
         * GP10: GPI    CD1
         * GP11: GPI    not used
         * GP12: GPI    ECNRTC_IN
         * GP13: GPI    not used
         * GP14: GPI    not used
         * GP15: GPI    not used
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W,
                   GP12_GPI | GP8_GPO);

        /*
         * pin   mode   comment
         * GP16: IORD   ISA bus
         * GP17: IOWR   ISA bus
         * GP18: IORDY  ISA bus
         * GP19: GPI    not used
         * GP20: GPI    not used
         * GP21: RESET  resets CS8900A
         * GP22: ROMCS0 ROM chip select
         * GP23: ROMCS1 ROM chip select
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
                   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
                   | GP18_IORDY | GP17_IOWR | GP16_IORD);
        /*
         * GP24: ROMCS2 ROM chip select
         * GP25: RxD1   SIU1
         * GP26: TxD1   SIU1
         * GP27: RTS1   SIU1
         * GP28: CTS1   SIU1
         * GP29: GPI    LED3
         * GP30: GPI    reserved by TANBAC TB0193
         * GP31: GPI    LED4
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
                   GP30_GPI
                   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
                   | GP24_ROMCS2);
#endif

#if 0
        /*
         * setup interrupt
         *
         * I4TYP:  falling edge trigger
         * GIMSK4: unmask
         * GIEN4:  enable
         * other:  unused, mask, disable
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTTYP_L_REG_W,
                   I4TYP_HIGH_LEVEL);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTMASK_REG_W,
                   0xffffU & ~GIMSK4);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTEN_REG_W, GIEN4);
#endif

        /*
         * programmable chip select
         *
         * PCS0 is used to select CS8900A Ethernet controller
         * on TB0193
         *
         * PCS0:
         *      0x14010000 - 0x14010fff
         *      I/O access, 16bit cycle, both of read/write
         * PCS1: unused
         */
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STRA_REG_W, 0x0000);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STPA_REG_W, 0x0fff);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0HIA_REG_W, 0x1401);
        REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCSMODE_REG_W,
                   PCS0MIOB_IO | PCS0DSIZE_16BIT | PCS0MD_READWRITE);
}

/*
 * chops the head from the arguments and returns the arguments if any,
 * or possibly an empty string.
 */
static char *
get_next_arg(char *arg)
{
        char *opt;

        if ((opt = strchr(arg, ' ')) == NULL) {
                opt = "";
        } else {
                *opt++ = '\0';
        }

        /* trim leading blanks */
        while (*opt == ' ')
                opt++;

        return opt;
}

static void
command_help(char *opt)
{
        printf("commands are:\n"
               "boot:\tb\n"
               "dump:\td addr [addr]\n"
               "fill:\tf addr addr char\n"
               "load:\tl [offset] (with following S-Record)\n"
               "write:\tw dst src len\n"
               "option:\to subcommand [params]\n"
               "help:\th|?\n"
               "\n"
               "option subcommands are:\n"
               "print:\to p\n"
               "read:\to r\n"
               "write:\to w\n"
               "path:\to path pathname\n"
               "bootp:\to bootp yes|no\n"
               "ip:\to ip remote local netmask gateway\n"
                );
}

static void
bad_param(void)
{
        printf("bad param\n");
        command_help(NULL);
}

static const u_int8_t print_cnv[] = {
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

static void
printhexul(u_int32_t n)
{
        int     i;

        for (i = 28; i >= 0; i -= 4)
                putchar(print_cnv[(n >> i) & 0x0f]);
}

static void
printhexuc(u_int8_t n)
{
        int     i;

        for (i = 4; i >= 0; i -= 4)
                putchar(print_cnv[(n >> i) & 0x0f]);
}

static void
command_dump(char *opt)
{
        char            *endptr;
        const char      *p;
        const char      *line_fence;
        const char      *limit;

        p = (const char *) strtoul(opt, &endptr, 16);
        if (opt == endptr) {
                bad_param();
                return;
        }

        opt = get_next_arg(opt);
        limit = (const char *) strtoul(opt, &endptr, 16);
        if (opt == endptr) {
                limit = p + 256;
        }

        for (;;) {
                printhexul((u_int32_t) p);
                putchar(' ');
                line_fence = p + 16;
                while (p < line_fence) {
                        printhexuc(*p++);
                        putchar(' ');
                        if (p >= limit) {
                                putchar('\n');
                                return;
                        }
                }
                putchar('\n');
                if (ISKEY) {
                        if (getchar() == '\x03')
                                break;
                }
        }
}

static void
command_boot(char *opt)
{
        u_long  marks[MARK_MAX];

        marks[MARK_START] = 0;
        if (loadfile(bootopts.b_pathname, marks, LOAD_KERNEL)) {
                printf("loadfile failed\n");
                return;
        }
        start_netbsd();
        /* no return */
}

/*
 * loading S-Record
 */
static int
load_srec(char *offset)
{
        char            s2lbuf[9];
        char            c;
        char            rectype;
        u_int32_t       reclen;
        u_int32_t       reclen_bk;
        u_int32_t       recaddr;
        char            *endptr;
        char            *p;
        u_int32_t       sum;
        int             err = 0;

        for (;;) {
                /*
                 * the first step is to read a S-Record.
                 */
                if ((c = getchar()) != 'S')
                        goto out;

                rectype = getchar();

                s2lbuf[0] = getchar();
                s2lbuf[1] = getchar();
                s2lbuf[2] = '\0';
                reclen_bk = reclen = strtoul(s2lbuf, &endptr, 16);
                if (endptr != &s2lbuf[2])
                        goto out;
                sum = reclen;

                p = s2lbuf;

                switch (rectype) {
                case '0':
                        /* just ignore */
                        do {
                                c = getchar();
                        } while (c != '\r' && c != '\n');
                        continue;

                case '3':
                        *p++ = getchar();
                        *p++ = getchar();
                        reclen--;
                        /* FALLTHRU */
                case '2':
                        *p++ = getchar();
                        *p++ = getchar();
                        reclen--;
                        /* FALLTHRU */
                case '1':
                        *p++ = getchar();
                        *p++ = getchar();
                        *p++ = getchar();
                        *p++ = getchar();
                        *p = '\0';
                        reclen -= 2;

                        recaddr = strtoul(s2lbuf, &endptr, 16);
                        if (endptr != p)
                                goto out;
                        sum += (recaddr >> 24) & 0xff;
                        sum += (recaddr >> 16) & 0xff;
                        sum += (recaddr >> 8) & 0xff;
                        sum += recaddr & 0xff;

                        p = offset + recaddr;
                        /*
                         * XXX
                         * address range is must be chaked here!
                         */
                        reclen--;
                        s2lbuf[2] = '\0';
                        while (reclen > 0) {
                                s2lbuf[0] = getchar();
                                s2lbuf[1] = getchar();
                                *p = (u_int8_t) strtoul(s2lbuf, &endptr, 16);
                                if (endptr != &s2lbuf[2])
                                        goto out;
                                sum += *p++;
                                reclen--;
                        }
                        break;

                case '7':
                case '8':
                case '9':
                        goto out2;

                default:
                        goto out;
                }

                s2lbuf[0] = getchar();
                s2lbuf[1] = getchar();
                s2lbuf[2] = '\0';
                sum += (strtoul(s2lbuf, &endptr, 16) & 0xff);
                sum &= 0xff;
                if (sum != 0xff) {
                        printf("checksum error\n");
                        err = 1;
                        goto out2;
                }

                c = getchar();
                if (c != '\r' && c != '\n')
                        goto out;
        }
        /* never reach */
        return 1;

out:
        printf("invalid S-Record\n");
        err = 1;

out2:
        do {
                c = getchar();
        } while (c != '\r' && c != '\n');

        return err;
}

static void
command_load(char *opt)
{
        char    *endptr;
        char    *offset;
        
        offset = (char *) strtoul(opt, &endptr, 16);
        if (opt == endptr)
                offset = 0;
        load_srec(offset);
}

static void
command_fill(char *opt)
{
        char    *endptr;
        char    *p;
        char    *limit;
        int     c;

        p = (char *) strtoul(opt, &endptr, 16);
        if (opt == endptr) {
                bad_param();
                return;
        }

        opt = get_next_arg(opt);
        limit = (char *) strtoul(opt, &endptr, 16);
        if (opt == endptr) {
                bad_param();
                return;
        }

        opt = get_next_arg(opt);
        c = strtoul(opt, &endptr, 16);
        if (opt == endptr)
                c = '\0';

        memset(p, c, limit - p);
}

static void
check_write_verify_flash(u_int32_t src, u_int32_t dst, size_t len)
{
        int             status;

        if ((dst & I28F128_BLOCK_MASK) != 0) {
                printf("dst addr must be aligned to block boundary (0x%x)\n",
                       I28F128_BLOCK_SIZE);
                return;
        }

        if (i28f128_probe((void *) dst)) {
                printf("dst addr is not a intel 28F128\n");
        } else {
                printf("intel 28F128 detected\n");
        }

        if ((status = i28f128_region_write((void *) dst, (void *) src, len))
            != 0) {
                printf("write mem to flash failed status = %x\n", status);
                return;
        }

        printf("verifying...");
        if (memcmp((void *) dst, (void *) src, len)) {
                printf("verify error\n");
                return;
        }
        printf("ok\n");

        printf("writing memory to flash succeeded\n");
}

static void
command_write(char *opt)
{
        char            *endptr;
        u_int32_t       src;
        u_int32_t       dst;
        size_t          len;

        dst = strtoul(opt, &endptr, 16);
        if (opt == endptr)
                goto out;

        opt = get_next_arg(opt);
        src = strtoul(opt, &endptr, 16);
        if (opt == endptr)
                goto out;

        opt = get_next_arg(opt);
        len = strtoul(opt, &endptr, 16);
        if (opt == endptr)
                goto out;

        check_write_verify_flash(src, dst, len);
        return;

out:
        bad_param();
        return;
}

static void
command_option(char *subcmd)
{
        char    *opt;
        int     i;

        opt = get_next_arg(subcmd);

        /* dispatch subcommand */
        for (i = 0; opt_subcommands[i].c_name != NULL; i++) {
                if (strcmp(subcmd, opt_subcommands[i].c_name) == 0) {
                        opt_subcommands[i].c_fn(opt);
                        break;
                }
        }
        if (opt_subcommands[i].c_name == NULL) {
                printf("unknown option subcommand\n");
                command_help(NULL);
        }
}

static void
opt_subcmd_print(char *opt)
{
        printf("boot options:\n"
               "magic:\t\t%s\n"
               "pathname:\t`%s'\n"
               "bootp:\t\t%s\n",
               bootopts.b_magic == BOOTOPT_MAGIC ? "ok" : "bad",
               bootopts.b_pathname,
               bootopts.b_flags & B_F_USE_BOOTP ? "yes" : "no");
        printf("remote IP:\t%s\n", inet_ntoa(bootopts.b_remote_ip));
        printf("local IP:\t%s\n", inet_ntoa(bootopts.b_local_ip));
        printf("netmask:\t%s\n", intoa(bootopts.b_netmask));
        printf("gateway IP:\t%s\n", inet_ntoa(bootopts.b_gate_ip));
}

static void
opt_subcmd_read(char *opt)
{
        bootopts = *((struct boot_option *) BOOTOPTS_BASE);
        if (bootopts.b_magic != BOOTOPT_MAGIC)
                bootopts.b_pathname[0] = '\0';
}

static void
opt_subcmd_write(char *opt)
{
        bootopts.b_magic = BOOTOPT_MAGIC;

        check_write_verify_flash((u_int32_t) &bootopts, BOOTOPTS_BASE,
                                 sizeof bootopts);
}

static void
opt_subcmd_path(char *opt)
{
        strlcpy(bootopts.b_pathname, opt, sizeof bootopts.b_pathname);
}

static void
opt_subcmd_bootp(char *opt)
{
        if (strcmp(opt, "yes") == 0) {
                bootopts.b_flags |= B_F_USE_BOOTP;
        } else if (strcmp(opt, "no") == 0) {
                bootopts.b_flags &= ~B_F_USE_BOOTP;
        } else {
                bad_param();
        }
}

static void
opt_subcmd_ip(char *opt)
{
        bootopts.b_remote_ip.s_addr = inet_addr(opt);
        opt = get_next_arg(opt);
        bootopts.b_local_ip.s_addr = inet_addr(opt);
        opt = get_next_arg(opt);
        bootopts.b_netmask = inet_addr(opt);
        opt = get_next_arg(opt);
        bootopts.b_gate_ip.s_addr = inet_addr(opt);
}

static void
bootmenu(void)
{
        char    input[LINEBUFLEN];
        char    *cmd;
        char    *opt;
        int     i;

        for (;;) {

                /* input a line */
                input[0] = '\0';
                printf("> ");
                kgets(input, sizeof(input));
                cmd = input;

                /* skip leading whitespace. */
                while(*cmd == ' ')
                        cmd++;

                if(*cmd) {
                        /* here, some command entered */

                        opt = get_next_arg(cmd);

                        /* dispatch command */
                        for (i = 0; commands[i].c_name != NULL; i++) {
                                if (strcmp(cmd, commands[i].c_name) == 0) {
                                        commands[i].c_fn(opt);
                                        break;
                                }
                        }
                        if (commands[i].c_name == NULL) {
                                printf("unknown command\n");
                                command_help(NULL);
                        }
                }
                
        }
}

static char
awaitkey(void)
{
        int     i;
        int     j;
        char    c = 0;

        while (ISKEY)
                getchar();

        for (i = BOOTTIMEOUT; i > 0; i--) {
                printf("%d\b", i);
                for (j = 0; j < 1000000; j++) {
                        if (ISKEY) {
                                while (ISKEY)
                                        c = getchar();
                                goto out;
                        }
                }
        }

out:
        printf("0\n");
        return(c);
}

__dead void
_rtt(void)
{
        for (;;)
                ;
}

int
main(void)
{
        char    c;

        init_devices();

        comcninit();

        opt_subcmd_read(NULL);

        print_banner();

        c = awaitkey();
        if (c != '\r' && c != '\n' && c != '\0') {
                printf("type \"?\" or \"h\" for help.\n");
                bootmenu(); /* does not return */
        }

        command_boot(NULL);
        /*
         * command_boot() returns only if it failed to boot.
         * we enter to boot menu in this case.
         */
        bootmenu();
        
        return 0;
}