/* -*-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);
}