root/sys/arch/hpc/stand/hpcboot/hpcmenu.cpp
/* -*-C++-*-    $NetBSD: hpcmenu.cpp,v 1.19 2010/04/06 16:20:27 nonaka Exp $    */

/*-
 * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * 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 THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <hpcmenu.h>
#include <hpcboot.h>
#include <res/resource.h>
#include <menu/window.h>
#include <menu/tabwindow.h>
#include <menu/rootwindow.h>
#include <menu/menu.h>
#include <machine/bootinfo.h>
#include <framebuffer.h>
#include <console.h>
#include <string.h>

HpcMenuInterface *HpcMenuInterface::_instance = 0;

HpcMenuInterface &
HpcMenuInterface::Instance()
{

        if (!_instance)
                _instance = new HpcMenuInterface();
        return *_instance;
}

void
HpcMenuInterface::Destroy()
{

        if (_instance)
                delete _instance;
}

HpcMenuInterface::HpcMenuInterface()
{

        if (!load())
                _set_default_pref();
        _pref._version  = HPCBOOT_VERSION;
        _pref._size     = sizeof(HpcMenuPreferences);

        _cons_parameter = 0;
        memset(_cons_hook, 0, sizeof(struct cons_hook_args) * 4);
        memset(&_boot_hook, 0, sizeof(struct boot_hook_args));
}

void
HpcMenuInterface::print(TCHAR *buf)
{

        if (_console)
                _console->print(buf);
}

void
HpcMenuInterface::get_options()
{

        _main->get();
        _option->get();
}

TCHAR *
HpcMenuInterface::dir(int i)
{
        int res = IDS_DIR_RES(i);

        if (!IDS_DIR_RES_VALID(res))
                return 0;

        if (_pref.dir_user && res == IDS_DIR_USER_DEFINED) {
                return _pref.dir_user_path;
        }

        TCHAR *s = reinterpret_cast <TCHAR *>
            (LoadString(_root->_app._instance, res, 0, 0));

        return s;
}

int
HpcMenuInterface::dir_default()
{

        return _pref.dir_user ? IDS_DIR_SEQ(IDS_DIR_USER_DEFINED) : 0;
}

void
HpcMenuInterface::_set_default_pref()
{

        _pref._magic            = HPCBOOT_MAGIC;
        _pref.dir               = 0;
        _pref.dir_user          = FALSE;
        _pref.kernel_user       = FALSE;
        _pref.platid_hi         = 0;
        _pref.platid_lo         = 0;
        _pref.rootfs            = 0;
        wsprintf(_pref.rootfs_file, TEXT("miniroot.fs"));
        _pref.boot_serial       = FALSE;
        _pref.boot_verbose      = FALSE;
        _pref.boot_single_user  = FALSE;
        _pref.boot_ask_for_name = FALSE;
        _pref.boot_debugger     = FALSE;
        wsprintf(_pref.boot_extra, TEXT(""));
        _pref.auto_boot         = 0;
        _pref.reverse_video     = FALSE;
        _pref.pause_before_boot = TRUE;
        _pref.safety_message    = TRUE;
#ifdef MIPS
        _pref.serial_speed      = 9600; // historical reason.
#else
        _pref.serial_speed      = 19200;
#endif
}

//
// load and save current menu status.
//
BOOL
HpcMenuInterface::load()
{
        TCHAR path[MAX_PATH];

        if (!_find_pref_dir(path))
                return FALSE;

        TCHAR filename[MAX_PATH];
        wsprintf(filename, TEXT("\\%s\\hpcboot.cnf"), path);
        HANDLE file = CreateFile(filename, GENERIC_READ, 0, 0, OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, 0);
        if (file == INVALID_HANDLE_VALUE)
                return FALSE;

        DWORD cnt;
        // read header.
        if (!ReadFile(file, &_pref, 12, &cnt, 0))
                goto bad;
        if (_pref._magic != HPCBOOT_MAGIC)
                goto bad;
        // read all.
        SetFilePointer(file, 0, 0, FILE_BEGIN);
        if (!ReadFile(file, &_pref, _pref._size, &cnt, 0))
                goto bad;
        CloseHandle(file);

        return TRUE;
 bad:
        CloseHandle(file);
        return FALSE;
}

BOOL
HpcMenuInterface::save()
{
        TCHAR path[MAX_PATH];

        if (_find_pref_dir(path)) {
                TCHAR filename[MAX_PATH];
                wsprintf(filename, TEXT("\\%s\\hpcboot.cnf"), path);
                HANDLE file = CreateFile(filename, GENERIC_WRITE, 0, 0,
                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
                DWORD cnt;
                WriteFile(file, &_pref, _pref._size, &cnt, 0);
                CloseHandle(file);
                return cnt == _pref._size;
        }

        return FALSE;
}

// arguments for kernel.
int
HpcMenuInterface::setup_kernel_args(vaddr_t v, paddr_t p, size_t sz)
{
        int argc = 0;
        kaddr_t *argv = reinterpret_cast <kaddr_t *>(v);
        char *loc = reinterpret_cast <char *>
            (v + sizeof(char **) * MAX_KERNEL_ARGS);
        paddr_t locp = p + sizeof(char **) * MAX_KERNEL_ARGS;
        size_t len;
        TCHAR *w;
        char *ptr;

#define SETOPT(c)                                                       \
__BEGIN_MACRO                                                           \
        argv[argc++] = ptokv(locp);                                     \
        *loc++ =(c);                                                    \
        *loc++ = '\0';                                                  \
        locp += 2;                                                      \
__END_MACRO
        // 1st arg is kernel name.
//      DPRINTF_SETUP();  //if you want to use debug print, enable this line.

        w = _pref.kernel_user_file;
        argv[argc++] = ptokv(locp);
        len = WideCharToMultiByte(CP_ACP, 0, w, wcslen(w), 0, 0, 0, 0);
        WideCharToMultiByte(CP_ACP, 0, w, len, loc, len, 0, 0);
        loc[len] = '\0';
        loc += len + 1;
        locp += len + 1;

        if (_pref.boot_serial)          // serial console
                SETOPT('h');
        if (_pref.boot_verbose)         // boot verbosely
                SETOPT('v');
        if (_pref.boot_single_user)     // boot to single user
                SETOPT('s');
        if (_pref.boot_ask_for_name)    // ask for file name to boot from
                SETOPT('a');
        if (_pref.boot_debugger)        // break into the kernel debugger
                SETOPT('d');

        // boot from
        switch(_pref.rootfs) {
        case 0: // wd0
                break;
        case 1: // sd0
                argv[argc++] = ptokv(locp);
                strncpy(loc, "b=sd0", 6);
                loc += 6;
                locp += 6;
                break;
        case 2: // memory disk
                w = _pref.rootfs_file;
                argv[argc++] = ptokv(locp);
                strncpy(loc, "m=", 2);
                loc += 2;
                len = WideCharToMultiByte(CP_ACP, 0, w, wcslen(w), 0, 0, 0, 0);
                WideCharToMultiByte(CP_ACP, 0, w, len, loc, len, 0, 0); 
                loc[len] = '\0';
                loc += len + 1;
                locp += 2 + len + 1;
                break;
        case 3: // nfs
                argv[argc++] = ptokv(locp);
                strncpy(loc, "b=nfs", 6);
                loc += 6;
                locp += 6;
                break;
        case 4: // dk0
                argv[argc++] = ptokv(locp);
                strncpy(loc, "b=dk0", 6);
                loc += 6;
                locp += 6;
                break;
        case 5: // ld0
                argv[argc++] = ptokv(locp);
                strncpy(loc, "b=ld0", 6);
                loc += 6;
                locp += 6;
                break;
        }

        // Extra kernel options. (Option tab window)
        w = _pref.boot_extra;
        len = WideCharToMultiByte(CP_ACP, 0, w, wcslen(w), 0, 0, 0, 0);

        if ((ptr = (char *)malloc(len)) == NULL) {
                MessageBox(_root->_window,
                    L"Can't allocate memory for extra kernel options.",
                    TEXT("WARNING"),
                    MB_ICONWARNING | MB_OK);
                UpdateWindow(_root->_window);

                return argc;
        }
        WideCharToMultiByte(CP_ACP, 0, w, len, ptr, len, 0, 0);
        ptr[len]='\0';

        while (*ptr == ' ' || *ptr == '\t')
                ptr++;
        while (*ptr != '\0') {
                len = strcspn(ptr, " \t");
                if (len == 0)
                        len = strlen(ptr);

                if (argc == MAX_KERNEL_ARGS || locp + len + 1 > p + sz) {
                        MessageBox(_root->_window,
                            L"Too many extra kernel options.",
                            TEXT("WARNING"),
                            MB_ICONWARNING | MB_OK);
                        UpdateWindow(_root->_window);
                        break;
                }
                argv[argc++] = ptokv(locp);
                strncpy(loc, ptr, len);
                loc[len] = '\0';
                loc += len + 1;
                locp += len + 1;

                ptr += len;
                while (*ptr == ' ' || *ptr == '\t')
                        ptr++;
        }

        return argc;
}

// kernel bootinfo.
void
HpcMenuInterface::setup_bootinfo(struct bootinfo &bi)
{
        FrameBufferInfo fb(_pref.platid_hi, _pref.platid_lo);
        TIME_ZONE_INFORMATION tz;
        DWORD tzid = GetTimeZoneInformation(&tz);

        memset(&bi, 0, sizeof(struct bootinfo));
        bi.length               = sizeof(struct bootinfo);
        bi.reserved             = 0;
        bi.magic                = BOOTINFO_MAGIC;
        bi.fb_addr              = fb.addr();
        bi.fb_type              = fb.type();
        bi.fb_line_bytes        = fb.linebytes();
        bi.fb_width             = fb.width();
        bi.fb_height            = fb.height();
        bi.platid_cpu           = _pref.platid_hi;
        bi.platid_machine       = _pref.platid_lo;
        bi.timezone             = tz.Bias;
        if (tzid == TIME_ZONE_ID_DAYLIGHT)
                bi.timezone    += tz.DaylightBias;
}

// Progress bar
void
HpcMenuInterface::progress(const char *msg)
{

        _root->progress(msg);
}

void
HpcMenuInterface::unprogress()
{

        _root->unprogress();
}

// Boot kernel.
void
HpcMenuInterface::boot()
{
        struct support_status *tab = _unsupported;
        uint32_t cpu = _pref.platid_hi;
        uint32_t machine = _pref.platid_lo;

        if (_pref.safety_message)
                for (; tab->cpu; tab++) {
                        if (tab->cpu == cpu && tab->machine == machine) {
                                MessageBox(_root->_window,
                                    tab->cause ? tab->cause :
                                    L"Not supported yet.",
                                    TEXT("BOOT FAILED"),
                                    MB_ICONERROR | MB_OK);
                                return;
                        }
                }

        if (_boot_hook.func)
                _boot_hook.func(_boot_hook.arg);
}