root/sys/arch/i386/stand/dosboot/main.c
/*      $NetBSD: main.c,v 1.34 2024/09/11 20:15:36 andvar Exp $  */

/*
 * Copyright (c) 1996, 1997
 *      Matthias Drochner.  All rights reserved.
 * Copyright (c) 1996, 1997
 *      Perry E. Metzger.  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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgements:
 *      This product includes software developed for the NetBSD Project
 *      by Matthias Drochner.
 *      This product includes software developed for the NetBSD Project
 *      by Perry E. Metzger.
 * 4. The names of the authors may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/reboot.h>

#include <lib/libkern/libkern.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/ufs.h>

#include <libi386.h>

int errno;

extern  char bootprog_name[], bootprog_rev[], bootprog_kernrev[];

#define MAXDEVNAME 16

static char    *current_fsmode;
static char    *default_devname;
static int      default_unit, default_partition;
static char    *default_filename;

char *sprint_bootsel(const char *);
static void bootit(const char *, int, int);
void usage(void);
int main(int, char **);

void    command_help(char *);
void    command_ls(char *);
void    command_quit(char *);
void    command_boot(char *);
void    command_mode(char *);
void    command_dev(char *);

const struct bootblk_command commands[] = {
        { "help",       command_help },
        { "?",          command_help },
        { "ls",         command_ls },
        { "quit",       command_quit },
        { "boot",       command_boot },
        { "mode",       command_mode },
        { "dev",        command_dev },
        { NULL,         NULL },
};

int
parsebootfile(const char *fname, char **fsmode, char **devname, int *unit, int *partition, const char **file)
        /* fsmode:  out */
        /* devname:  out */
        /* unit, *partition:  out */
        /* file:  out */
{
        const char     *col, *help;

        *fsmode = current_fsmode;
        *devname = default_devname;
        *unit = default_unit;
        *partition = default_partition;
        *file = default_filename;

        if (fname == NULL)
                return (0);

        if (strcmp(current_fsmode, "dos") && (col = strchr(fname, ':'))) {
                /* no DOS, device given */
                static char     savedevname[MAXDEVNAME + 1];
                int             devlen;
                unsigned int    u = 0, p = 0;
                int             i = 0;

                devlen = col - fname;
                if (devlen > MAXDEVNAME)
                        return (EINVAL);

#define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
                if (!isvalidname(fname[i]))
                        return (EINVAL);
                do {
                        savedevname[i] = fname[i];
                        i++;
                } while (isvalidname(fname[i]));
                savedevname[i] = '\0';

#define isnum(c) ((c) >= '0' && (c) <= '9')
                if (i < devlen) {
                        if (!isnum(fname[i]))
                                return (EUNIT);
                        do {
                                u *= 10;
                                u += fname[i++] - '0';
                        } while (isnum(fname[i]));
                }

#define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
                if (i < devlen) {
                        if (!isvalidpart(fname[i]))
                                return (EPART);
                        p = fname[i++] - 'a';
                }
                if (i != devlen)
                        return (ENXIO);

                *devname = savedevname;
                *unit = u;
                *partition = p;
                help = col + 1;
        } else
                help = fname;

        if (*help)
                *file = help;

        return (0);
}

char *
sprint_bootsel(const char *filename)
{
        char *fsname, *devname;
        int unit, partition;
        const char *file;
        static char buf[80];
        
        if (parsebootfile(filename, &fsname, &devname, &unit,
                          &partition, &file) == 0) {
                if (!strcmp(fsname, "dos"))
                        snprintf(buf, sizeof(buf), "dos:%s", file);
                else if (!strcmp(fsname, "ufs"))
                        snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
                                'a' + partition, file);
                else goto bad;
                return (buf);
        }
bad:
        return ("(invalid)");
}

static void
bootit(const char *filename, int howto, int tell)
{
        int floppy = strncmp(default_devname, "fd", 2) == 0;
        if (tell) {
                printf("booting %s", sprint_bootsel(filename));
                if (howto)
                        printf(" (howto 0x%x)", howto);
                printf("\n");
        }
        if (exec_netbsd(filename, 0, howto, floppy, NULL) < 0)
                printf("boot: %s: %s\n", sprint_bootsel(filename),
                       strerror(errno));
        else
                printf("boot returned\n");
}

static void
print_banner(void)
{
        int extmem = getextmem();

        clear_pc_screen();

        printf("\n"
               ">> %s, Revision %s (from NetBSD %s)\n"
               ">> Memory: %d/%d k\n",
               bootprog_name, bootprog_rev, bootprog_kernrev,
               getbasemem(), extmem);
}

void 
usage(void)
{
        printf("dosboot [-u] [-c <commands>] [-i] [filename [-bootopts]]\n");
}

int 
main(int argc, char **argv)
{
        int             ch;
        int             interactive = 0;
        int             howto;
        extern char    *optarg;
        extern int      optind;

#ifdef  SUPPORT_SERIAL
        initio(SUPPORT_SERIAL);
#else
        initio(CONSDEV_PC);
#endif
        gateA20();

        print_banner();

        current_fsmode = "dos";
        default_devname = "hd";
        default_unit = 0;
        default_partition = 0;
        default_filename = "netbsd";

        while ((ch = getopt(argc, argv, "c:iu")) != -1) {
                switch (ch) {
                case 'c':
                        docommand(optarg);
                        return (1);
                        break;
                case 'i':
                        interactive = 1;
                        break;
                case 'u':
                        current_fsmode = "ufs";
                        break;
                default:
                        usage();
                        return (1);
                }
        }

        if (interactive) {
                printf("type \"?\" or \"help\" for help.\n");
                bootmenu();
        }

        argc -= optind;
        argv += optind;

        if (argc > 2) {
                usage();
                return (1);
        }
        howto = 0;
        if (argc > 1 && !parseopts(argv[1], &howto))
                return (1);

        bootit((argc > 0 ? argv[0] : "netbsd"), howto, 1);
        return (1);
}

/* ARGSUSED */
void
command_help(char *arg)
{
        printf("commands are:\n"
               "boot [xdNx:][filename] [-acdqsv]\n"
               "     (ex. \"sd0a:netbsd.old -s\"\n"
               "ls [path]\n"
               "mode ufs|dos\n"
               "dev xd[N[x]]:\n"
               "help|?\n"
               "quit\n");
}

void
command_ls(char *arg)
{
        char *help = default_filename;
        default_filename = "/";
        ls(arg);
        default_filename = help;
}

/* ARGSUSED */
void
command_quit(char *arg)
{
        printf("Exiting... goodbye...\n");
        _rtt();
}

void
command_boot(char *arg)
{
        char *filename;
        int howto;

        if (parseboot(arg, &filename, &howto))
                bootit(filename, howto, 1);
}

void
command_mode(char *arg)
{
        if (!strcmp("dos", arg))
                current_fsmode = "dos";
        else if (!strcmp("ufs", arg))
                current_fsmode = "ufs";
        else
                printf("invalid mode\n");
}

void
command_dev(char *arg)
{
        static char savedevname[MAXDEVNAME + 1];
        char *fsname, *devname;
        const char *file; /* dummy */

        if (!strcmp(current_fsmode, "dos")) {
                printf("not available in DOS mode\n");
                return;
        }

        if (*arg == '\0') {
                printf("%s%d%c:\n", default_devname, default_unit,
                       'a' + default_partition);
                return;
        }

        if (!strchr(arg, ':') ||
            parsebootfile(arg, &fsname, &devname, &default_unit,
                          &default_partition, &file)) {
                command_help(NULL);
                return;
        }
            
        /* put to own static storage */
        strncpy(savedevname, devname, MAXDEVNAME + 1);
        default_devname = savedevname;
}