VBoxBFE.cpp revision 9d3a6dcdf5659c385fd193a59927d74c92ce401d
/** @file
*
* VBox frontends: Basic Frontend (BFE):
* VBoxBFE main routines
*
* VBoxBFE is a limited frontend that sits directly on the Virtual Machine
* Manager (VMM) and does _not_ use COM to communicate.
* On Linux and Windows, VBoxBFE is based on SDL; on L4 it's based on the
* L4 console. Much of the code has been copied over from the other frontends
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_GUI
#ifndef VBOXBFE_WITHOUT_COM
using namespace com;
#endif
#ifdef VBOXBFE_WITH_USB
#endif
#include <iprt/semaphore.h>
#include "VBoxBFE.h"
#include <stdio.h>
#include <stdlib.h> /* putenv */
#include <errno.h>
#include <fcntl.h>
#endif
#ifndef __L4ENV__
#include <vector>
#endif
#include "ConsoleImpl.h"
#include "DisplayImpl.h"
#include "MouseImpl.h"
#include "KeyboardImpl.h"
#include "VMMDevInterface.h"
#include "StatusImpl.h"
#include "Framebuffer.h"
#include "MachineDebuggerImpl.h"
#ifdef VBOXBFE_WITH_USB
# include "HostUSBImpl.h"
#endif
#include "SDLConsole.h"
#include "SDLFramebuffer.h"
#endif
#ifdef __L4__
#include "L4Console.h"
#include "L4Framebuffer.h"
#endif
#ifdef __L4ENV__
# ifndef L4API_l4v2onv4
# endif
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define VBOXSDL_ADVANCED_OPTIONS
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(void) vmstateChangeCallback(PVM pVM, VMSTATE enmState, VMSTATE enmOldState, void *pvUser);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef VBOXBFE_WITH_USB
#endif
#ifdef __L4__
int gHostKey; /* not used */
int gHostKeySym = KEY_RIGHTCTRL;
#elif defined (DEBUG_dmik)
// my mini kbd doesn't have RCTRL...
int gHostKey = KMOD_RSHIFT;
int gHostKeySym = SDLK_RSHIFT;
#else
int gHostKey = KMOD_RCTRL;
int gHostKeySym = SDLK_RCTRL;
#endif
bool gfAllowFullscreenToggle = true;
static bool g_fIOAPIC = false;
static bool fACPI = true;
static bool fAudio = false;
#ifdef VBOXBFE_WITH_USB
static bool fUSB = false;
#endif
//static bool fPacketSniffer = false;
static char *pszBootDevice = "IDE";
#ifdef VBOXSDL_ADVANCED_OPTIONS
static unsigned fRawR0 = ~0U;
static unsigned fRawR3 = ~0U;
static unsigned fPATM = ~0U;
static unsigned fCSAM = ~0U;
#endif
static bool g_fReleaseLog = true; /**< Set if we should open the release. */
/**
* Network device config info.
*/
typedef struct BFENetworkDevice
{
enum
{
NOT_CONFIGURED = 0,
NONE,
NAT,
HIF,
} enmType; /**< The type of network driver. */
bool fSniff; /**< Set if the network sniffer should be installed. */
const char *pszSniff; /**< Output file for the network sniffer. */
const char *pszName; /**< The device name of a HIF device. The name of the internal network. */
#if 1//defined(__LINUX__)
bool fHaveFd; /**< Set if fd is valid. */
#endif
} BFENETDEV, *PBFENETDEV;
/** Array of network device configurations. */
/** @todo currently this is only set but never read. */
static char szError[512];
/**
* Converts the passed in network option
*
* @returns Index into g_aNetDevs on success. (positive)
* @returns VERR_INVALID_PARAMETER on failure. (negative)
* @param pszArg The argument.
* @param cchRoot The length of the argument root.
*/
{
uint32_t n;
if (VBOX_FAILURE(rc))
{
return -1;
}
if (n < 1 || n > NetworkAdapterCount)
{
RTPrintf("Error: The network device number is out of range: %RU32 (1 <= 0 <= %u) (%s)\n",
n, NetworkAdapterCount, pszArg);
return -1;
}
return n;
}
/**
* Print a syntax error.
*
* @returns return value for main().
* @param pszMsg The message format string.
* @param ... Format arguments.
*/
static int SyntaxError(const char *pszMsg, ...)
{
RTPrintf("error: ");
return 1;
}
/**
* Print a fatal error.
*
* @returns return value for main().
* @param pszMsg The message format string.
* @param ... Format arguments.
*/
static int FatalError(const char *pszMsg, ...)
{
RTPrintf("fatal error: ");
return 1;
}
/**
* Print program usage.
*/
static void show_usage()
{
RTPrintf("Usage:\n"
" -hda <file> Set first hard disk to file\n"
" -fda <file> Set first floppy disk to file\n"
" -boot <a|c|d> Set boot device (a = floppy, c = first hard disk, d = DVD)\n"
" -m <size> Set memory size in megabytes (default 128MB)\n"
" -vram <size> Set size of video memory in megabytes\n"
" -fullscreen Start VM in fullscreen mode\n"
" -nohostkey Disable hostkey\n"
" -[no]acpi Enable or disable ACPI (default: enabled)\n"
" -[no]ioapic Enable or disable the IO-APIC (default: disabled)\n"
" -audio Enable audio\n"
" -natdev<1-N> Configure NAT for network device N\n"
" -hifdev<1-N> <dev> <mac> Use existing Host Interface Network Device with the given name and MAC address\n"
#if 0
" -netsniff<1-N> Enable packet sniffer\n"
#endif
#ifdef __LINUX__
" -tapfd<1-N> <fd> Use existing TAP device, don't allocate\n"
#endif
#ifdef VBOX_VRDP
" -vrdp [port] Listen for VRDP connections on port (default if not specified)\n"
#endif
#ifdef VBOX_SECURELABEL
" -securelabel Display a secure VM label at the top of the screen\n"
" -seclabelfnt TrueType (.ttf) font file for secure session label\n"
" -seclabelsiz Font point size for secure session label (default 12)\n"
#endif
" -[no]rellog Enable or disable the release log './VBoxBFE.log' (default: enabled)\n"
#ifdef VBOXSDL_ADVANCED_OPTIONS
" -[no]rawr0 Enable or disable raw ring 3\n"
" -[no]rawr3 Enable or disable raw ring 0\n"
" -[no]patm Enable or disable PATM\n"
" -[no]csam Enable or disable CSAM\n"
#endif
#ifdef __L4ENV__
" -env <var=value> Set the given environment variable to \"value\"\n"
#endif
"\n");
}
/** entry point */
{
#ifdef __L4ENV__
#ifndef L4API_l4v2onv4
/* clear Fiasco kernel trace buffer */
#endif
/* set the environment. Must be done before the runtime is
initialised. Yes, it really must. */
for (int i = 0; i < argc; i++)
{
if (++i >= argc)
return SyntaxError("missing argument to -env (format: var=value)!\n");
/* add it to the environment */
}
#endif /* __L4ENV__ */
/*
* Before we do *anything*, we initialize the runtime.
*/
if (VBOX_FAILURE(rc))
bool fFullscreen = false;
#ifdef VBOX_VRDP
#endif
#ifdef VBOX_SECURELABEL
bool fSecureLabel = false;
char *secureLabelFontFile = NULL;
#endif
// less than one parameter is not possible
if (argc < 2)
{
show_usage();
return 1;
}
/*
* Parse the command line arguments.
*/
{
{
return SyntaxError("missing argument for boot drive!\n");
rc = VINF_SUCCESS;
{
case 'a':
{
pszBootDevice = "FLOPPY";
break;
}
case 'c':
{
pszBootDevice = "IDE";
break;
}
case 'd':
{
pszBootDevice = "DVD";
break;
}
default:
}
}
{
return SyntaxError("missing argument for memory size!\n");
if (VBOX_FAILURE(rc))
return SyntaxError("cannot grok the memory size: %s (%Vrc)\n",
}
{
return SyntaxError("missing argument for vram size!\n");
if (VBOX_FAILURE(rc))
return SyntaxError("cannot grok the vram size: %s (%Vrc)\n",
}
{
fFullscreen = true;
}
{
gfAllowFullscreenToggle = false;
}
{
gHostKey = 0;
gHostKeySym = 0;
}
{
fACPI = true;
}
{
fACPI = false;
}
{
g_fIOAPIC = true;
}
{
g_fIOAPIC = false;
}
{
fAudio = true;
}
#ifdef VBOXBFE_WITH_USB
{
fUSB = true;
}
#endif
{
return SyntaxError("missing file name for first hard disk!\n");
/* resolve it. */
if (!hdaFile)
return SyntaxError("The path to the specified harddisk, '%s', could not be resolved.\n", argv[curArg]);
}
{
return SyntaxError("missing file/device name for first floppy disk!\n");
/* resolve it. */
if (!fdaFile)
return SyntaxError("The path to the specified floppy disk, '%s', could not be resolved.\n", argv[curArg]);
}
{
return SyntaxError("missing file/device name for first hard disk!\n");
/* resolve it. */
if (!cdromFile)
return SyntaxError("The path to the specified cdrom, '%s', could not be resolved.\n", argv[curArg]);
}
{
if (i < 0)
return 1;
/* The HIF device name / The Internal Network name. */
{
? "The TAP network device name is missing! (%s)\n"
: "The internal network name is missing! (%s)\n"
, pszArg);
}
/* The MAC address. */
return SyntaxError("The network MAC address has an invalid length: %s (%s)\n", argv[curArg], pszArg);
{
if (c1 > 9)
c1 -= 7;
if (c2 > 9)
c2 -= 7;
}
}
{
if (rc < 0)
return 1;
g_aNetDevs[i].fSniff = true;
/** @todo filename */
}
#ifdef __LINUX__
{
if (VBOX_FAILURE(rc))
g_aNetDevs[i].fHaveFd = true;
}
#endif /* __LINUX__ */
#ifdef VBOX_VRDP
{
// -vrdp might take a port number (positive).
portVRDP = 0; // indicate that it was encountered.
{
if (VBOX_FAILURE(rc))
}
}
#endif /* VBOX_VRDP */
#ifdef VBOX_SECURELABEL
{
fSecureLabel = true;
LogFlow(("Secure labelling turned on\n"));
}
{
return SyntaxError("missing font file name for secure label!\n");
}
{
return SyntaxError("missing font point size for secure label!\n");
}
#endif
{
g_fReleaseLog = true;
}
{
g_fReleaseLog = false;
}
#ifdef VBOXSDL_ADVANCED_OPTIONS
{
fRawR0 = true;
}
{
fRawR0 = false;
}
{
fRawR3 = true;
}
{
fRawR3 = false;
}
{
fPATM = true;
}
{
fPATM = false;
}
{
fCSAM = true;
}
{
fCSAM = false;
}
#endif /* VBOXSDL_ADVANCED_OPTIONS */
#ifdef __L4__
++curArg;
#endif /* __L4__ */
/* just show the help screen */
else
{
show_usage();
return 1;
}
}
gMachineDebugger = new MachineDebugger();
#if defined(USE_SDL)
/* First console, then framebuffer!! */
gConsole = new SDLConsole();
gFramebuffer = new SDLFramebuffer();
gFramebuffer = new L4Framebuffer();
#else
#error "todo"
#endif
if (!gConsole->initialized())
goto leave;
/* start with something in the titlebar */
/*
* Start the VM execution thread. This has to be done
* asynchronously as powering up can take some time
* (accessing devices such as the host DVD drive). In
* the meantime, we have to service the SDL event loop.
*/
if (VBOX_FAILURE(rc))
{
return -1;
}
/* loop until the powerup processing is done */
do
{
if ( machineState == VMSTATE_CREATING
|| machineState == VMSTATE_LOADING)
{
switch (event)
{
LogFlow(("CONEVENT_USR_SCREENRESIZE\n"));
gFramebuffer->resize();
/* notify the display that the resize has been completed */
break;
case CONEVENT_USR_QUIT:
RTPrintf("Error: failed to power up VM! No error text available.\n");
goto leave;
}
}
else
#endif
RTThreadSleep(1000);
}
while ( machineState == VMSTATE_CREATING
|| machineState == VMSTATE_LOADING);
if (machineState == VMSTATE_TERMINATED)
goto leave;
/* did the power up succeed? */
if (machineState != VMSTATE_RUNNING)
{
RTPrintf("Error: failed to power up VM! No error text available (rc = 0x%x state = %d)\n", rc, machineState);
goto leave;
}
/*
* Main event loop
*/
LogFlow(("VBoxSDL: Entering big event loop\n"));
while (1)
{
switch (event)
{
case CONEVENT_NONE:
/* Handled internally */
break;
case CONEVENT_QUIT:
case CONEVENT_USR_QUIT:
goto leave;
case CONEVENT_SCREENUPDATE:
/// @todo that somehow doesn't seem to work!
gFramebuffer->repaint();
break;
break;
{
LogFlow(("CONEVENT_USR_SCREENRESIZE\n"));
gFramebuffer->resize();
/* notify the display that the resize has been completed */
break;
}
#ifdef VBOX_SECURELABEL
{
/*
* Query the new label text
*/
/*
* Now update the label
*/
break;
}
#endif /* VBOX_SECURELABEL */
}
}
LogFlow(("Returning from main()!\n"));
if (pVM)
{
/*
* If get here because the guest terminated using ACPI off we don't have to
* switch off the VM because we were notified via vmstateChangeCallback()
* that this already happened. In any other case stop the VM before killing her.
*/
if (machineState != VMSTATE_OFF)
{
/* Power off VM */
}
/* And destroy it */
}
delete gFramebuffer;
delete gConsole;
delete gDisplay;
delete gKeyboard;
delete gMouse;
delete gStatus;
delete gMachineDebugger;
}
/**
* VM state callback function. Called by the VMM
* using its state machine states.
*
* Primarily used to handle VM initiated power off, suspend and state saving,
* but also for doing termination completed work (VMSTATE_TERMINATE).
*
* In general this function is called in the context of the EMT.
*
* @todo machineState is set to VMSTATE_RUNNING before all devices have received power on events
* this can prematurely allow the main thread to enter the event loop
*
* @param pVM The VM handle.
* @param enmState The new state.
* @param enmOldState The old state.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) vmstateChangeCallback(PVM pVM, VMSTATE enmState, VMSTATE enmOldState, void *pvUser)
{
switch (enmState)
{
/*
* The VM has terminated
*/
case VMSTATE_OFF:
{
break;
}
/*
* The VM has been completely destroyed.
*
* Note: This state change can happen at two points:
* 1) At the end of VMR3Destroy() if it was not called from EMT.
* 2) At the end of vmR3EmulationThread if VMR3Destroy() was called by EMT.
*/
case VMSTATE_TERMINATED:
{
break;
}
default: /* shut up gcc */
break;
}
}
/**
* VM error callback function. Called by the various VM components.
*
* @param pVM The VM handle.
* @param pvUser The user argument.
* @param rc VBox status code.
* @param pszError Error message format string.
* @param args Error message arguments.
* @thread EMT.
*/
{
/** @todo accessing shared resource without any kind of synchronization */
if (VBOX_SUCCESS(rc))
szError[0] = '\0';
else
}
/** VM asynchronous operations thread */
{
int rc = VINF_SUCCESS;
int rc2;
/*
* Setup the release log instance in current directory.
*/
if (g_fReleaseLog)
{
static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
RTLOGDEST_FILE, "./VBoxBFE.log");
if (VBOX_SUCCESS(rc2))
{
/* some introductory information */
char szNowUct[64];
RTLogRelLogger(pLogger, 0, ~0U,
"VBoxBFE %s (%s %s) release log\n"
"Log opened %s\n",
szNowUct);
/* register this logger as the release logger */
}
}
/*
* Start VM (also from saved state) and track progress
*/
LogFlow(("VMPowerUp\n"));
/*
* Create empty VM.
*/
if (VBOX_FAILURE(rc))
{
goto failure;
}
/*
* Register VM state change handler
*/
if (VBOX_FAILURE(rc))
{
goto failure;
}
#ifdef VBOXBFE_WITH_USB
/*
* Capture USB devices.
*/
if (fUSB)
{
}
#endif /* VBOXBFE_WITH_USB */
#ifdef __L4ENV__
/* L4 console cannot draw a host cursor */
gMouse->setHostCursor(false);
#else
gMouse->setHostCursor(true);
#endif
/*
* Power on the VM (i.e. start executing).
*/
if (VBOX_SUCCESS(rc))
{
if (VBOX_SUCCESS(rc))
{
}
else
}
/*
* On failure destroy the VM.
*/
if (VBOX_FAILURE(rc))
{
goto failure;
}
return 0;
if (pVM)
{
}
return 0;
}
/**
* Register the main drivers.
*
* @returns VBox status code.
* @param pCallbacks Pointer to the callback table.
* @param u32Version VBox version number.
*/
{
int rc;
AssertReleaseMsg(u32Version == VBOX_VERSION, ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION));
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Creates the default configuration.
* This assumes an empty tree.
*
* @returns VBox status code.
* @param pVM VM handle.
*/
{
int rcAll = VINF_SUCCESS;
int rc;
/*
* Create VM default values.
*/
UPDATERC();
UPDATERC();
UPDATERC();
#ifdef VBOXSDL_ADVANCED_OPTIONS
UPDATERC();
UPDATERC();
UPDATERC();
#else
UPDATERC();
UPDATERC();
UPDATERC();
#endif
UPDATERC();
/*
* PDM.
*/
UPDATERC();
/*
* Devices
*/
UPDATERC();
/* device */
/*
* PC Arch.
*/
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
/*
* PC Bios.
*/
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
/* Default: no bios logo. */
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
/*
* ACPI
*/
if (fACPI)
{
}
/*
* PCI bus.
*/
UPDATERC();
UPDATERC();
UPDATERC();
UPDATERC();
/*
* DMA
*/
/*
* PCI bus.
*/
/*
* PS/2 keyboard & mouse.
*/
/*
* i82078 Floppy drive controller
*/
/* Attach the status driver */
if (fdaFile)
{
}
/*
* i8254 Programmable Interval Timer And Dummy Speaker
*/
#ifdef DEBUG
#endif
/*
* i8259 Programmable Interrupt Controller.
*/
/*
* Advanced Programmable Interrupt Controller.
*/
/*
* I/O Advanced Programmable Interrupt Controller.
*/
if (g_fIOAPIC)
{
}
/*
* RTC MC146818.
*/
/*
* Serial ports
*/
/*
* VGA.
*/
#ifdef __L4ENV__
/* XXX hard-coded */
char szBuf[64];
/* Tell the guest which is the ideal video mode to use */
#endif
/*
* IDE (update this when the main interface changes)
*/
if (hdaFile)
{
}
if (cdromFile)
{
// ASSUME: DVD drive is always attached to LUN#2 (i.e. secondary IDE master)
}
/*
* Network adapters
*/
{
{
char szInstance[4];
CHECK_RC();
/*
* Enable the packet sniffer if requested.
*/
{
/* insert the sniffer filter driver. */
{
}
}
/*
* Create the driver config (if any).
*/
{
{
}
else
{
}
}
/*
* Configure the driver.
*/
{
/* (Port forwarding goes here.) */
}
{
#if defined(__LINUX__)
{
}
else
#endif
{
/*
*/
if (VBOX_FAILURE(rc))
{
return rc;
}
{
{
FatalError("HIF name too long for device #%d: %s\n",
return VERR_BUFFER_OVERFLOW;
}
}
else
if (rc)
{
FatalError("ioctl TUNSETIFF '%s' failed: errno=%d rc=%d (%Vrc)\n",
return rc2;
}
if (rc)
{
FatalError("fcntl F_SETFL/O_NONBLOCK '%s' failed: errno=%d rc=%d (%Vrc)\n",
return rc2;
}
/*
* We need the GUID too here...
*/
#else /* !__LINUX__ && !__L4__ */
FatalError("Name based HIF devices not implemented yet for this host platform\n");
return VERR_NOT_IMPLEMENTED;
#endif
}
}
{
/*
* Internal networking.
*/
}
}
}
/*
* VMM Device
*/
/* the VMM device's Main driver */
/*
* AC'97 ICH audio
*/
if (fAudio)
{
/* the Audio driver */
#ifdef __WIN__
#else /* !__WIN__ */
#endif /* !__WIN__ */
}
#ifdef VBOXBFE_WITH_USB
/*
* The USB Controller.
*/
if (fUSB)
{
}
#endif /* VBOXBFE_WITH_USB */
return rc;
}