2.14. The Bochs plugin interface

2.14.1. Introduction

By default Bochs is compiled as one single executable file providing all features enabled at compile time. To add a new feature (device, gui, driver) several files need to be modified (configure script, makefile, some headers) and the complete build process must be performed. As an alternative, Bochs can be compiled as an executable file containing the core funtionality and a number of loadable modules providing config interface, device emulation, display library (gui) capabilities or drivers for some other features. On Windows platforms such a module is called DLL, other platforms like Linux call it shared library and use libtool to create it. Bochs uses the environment variable LTDL_LIBARY_PATH to search for plugins. To build Bochs in this mode, the configure option --enable-plugins must be used. In this mode it is possible to add externally developed extensions (AKA "user plugins") that can be detected at startup. Rebuilding the whole project is not necessary in that case. Basically these Bochs facilities are currently supported by the plugin interface:

2.14.2. Plugin file names

The plugin interface expects a special file name format that depends on the plugin type and platform. On Linux and other platforms using libtool-based compilation the plugin file for the CMOS device gets this name:

libbx_cmos.so
On Windows platforms (Cygwin, MinGW/MSYS, MSVC) the output file is a DLL and the name is slightly different:
bx_cmos.dll
The names of device plugins are created from the base name of source files like the example above. For other module types the naming is similar, but with some extensions. This table shows how the names of some existing modules are created:

Table 2-11. Plugin file naming

TypeModule nameSource file nameLibtool file nameDLL file name
Display librarysdl2sdl2.cclibbx_sdl2_gui.sobx_sdl2_gui.dll
Disk image modulevboxvbox.cclibbx_vbox_img.sobx_vbox_img.dll
Networking moduleslirpeth_slirp.cclibbx_eth_slirp.sobx_eth_slirp.dll
Lowlevel sound driverfilesoundfile.cclibbx_soundfile.sobx_soundfile.dll

2.14.3. Plugin types

Device plugins are categorized into some types depending on their purpose. Some devices are mandatory for the x86 PC emulation or need to be initialized early to make other devices work correctly. A plugin can be loaded only one single time, but some types may be capable to create multiple objects. This is the full list of plugin types defined in extplugin.h with some description:

Table 2-12. Plugin types

TypeDescriptionEntry function nameBase C++ class usedRemarks
PLUGTYPE_CORECore device plugin, always required or depending on core option, highest priority in init orderlibmodule_plugin_entrybx_devmodel_cSingle device only
PLUGTYPE_STANDARDDevice plugin, mostly required or depending on core plugins, lower priority in init orderlibmodule_plugin_entrybx_devmodel_cSingle device only
PLUGTYPE_OPTIONALDevice plugin depending on normal config optionlibmodule_plugin_entrybx_devmodel_cNetwork device plugins can create up to 4 instances
PLUGTYPE_VGAVGA-compatible device plugin selected with vga: extension=X optionlibmodule_plugin_entrybx_vgacore_cOne plugin of type required / supported
PLUGTYPE_USBUSB device plugin selected with the portX parameter of the host controller optionlibmodule_plugin_entryusb_device_cNumber of instances not limited
PLUGTYPE_CIConfig interface plugin selected with the config_interface optionlibmodule_gui_plugin_entry-One plugin of type required / supported
PLUGTYPE_GUIDisplay library (gui) plugin selected with the display_library optionlibmodule_gui_plugin_entrybx_gui_cOne plugin of type required / supported
PLUGTYPE_IMGAdditional disk image format selected with the "mode" parameter when setting up a disk imagelibmodule_img_plugin_entrydevice_image_tNumber of instances not limited
PLUGTYPE_NETNetworking driver / emulation module selected with the "ethmod" parameter of the NIC optionslibeth_module_plugin_entryeth_pktmover_cSome modules can create multiple instances
PLUGTYPE_SNDSound driver selected with the sound optionlibsoundmodule_plugin_entrybx_sound_lowlevel_cOne plugin of type required / max. 4 different plugins supported

2.14.4. The plugin entry function

Each plugin has an entry function that is called during plugin detection, after loading and before unloading the modules. For compatiblity with the "monolithic" Bochs compilation each plugin must have a unique name. When plugins are disabled, the macros / functions for loading / unloading the plugin directly call the plugin entry function. The entry function can be called with these mode arguments:

At Bochs startup, but before initializing the config options, it searches the plugin paths for modules with the correct file name format and the expected entry function name. Each valid plugin is temporarily loaded to read the plugin type and flags by calling the entry function with the corresponding mode. The plugin interface builds up a database with all information for loading detected modules later on demand. A plugin can return multiple types, but it can only be loaded with one of it (currently used by the "wx" gui and "voodoo" device plugin). The flags can be used to indicate specific capabilities of the plugin (currently on used for device plugins that can be connected to a PCI slot). For unimplemented calling modes the entry function must return 0.

To simplify the naming of the plugin entry function some macros have been defined. This example shows how the entry function is defined for normal device plugins on platforms other than Windows:

#define PLUGIN_ENTRY_FOR_MODULE(mod) \
  extern "C" int CDECL lib##mod##_plugin_entry(plugin_t *plugin, Bit16u type, Bit8u mode)
Please see plugin.h for all supported definitions of the entry function. The example below shows the plugin-related section of the "unmapped" device source file.
// Define BX_PLUGGABLE in files that can be compiled into plugins.  For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#include "unmapped.h"

#define LOG_THIS theUnmappedDevice->

bx_unmapped_c *theUnmappedDevice = NULL;

PLUGIN_ENTRY_FOR_MODULE(unmapped)
{
  if (mode == PLUGIN_INIT) {
    theUnmappedDevice = new bx_unmapped_c();
    BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUnmappedDevice, BX_PLUGIN_UNMAPPED);
  } else if (mode == PLUGIN_FINI) {
    delete theUnmappedDevice;
  } else if (mode == PLUGIN_PROBE) {
    return (int)PLUGTYPE_OPTIONAL;
  }
  return(0); // Success
}

2.14.5. Compatibility with "monolithic" Bochs compilation

To ensure compatibility between both compilation modes a bunch of macros have been defined in plugin.h. If required the specific functions are implemented in plugin.cc. That's why the code for the modules that can be plugins doesn't need special cases for "plugin" and "non-plugin" mode. For the plugin types PLUGTYPE_CORE and PLUGTYPE_STANDARD the macros for loading / unloading plugin directly call the entry function. For the other types a static list is created at compile time using a modified version of the plugin_t structure. This is the counterpart to the dynamic list in plugin mode created at startup. The load / unload functions are similar in both modes, except that the "non-plugin" version of these functions finally just call the entry function. These macros are defined for both modes, but calling mode specific code:

PLUG_load_plugin(name,type)
PLUG_get_plugins_count(type)
PLUG_get_plugin_name(type,index)
PLUG_get_plugin_flags(type,index)
PLUG_load_plugin_var(name,type)
PLUG_load_opt_plugin(name)
PLUG_unload_opt_plugin(name)