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:
config interface
i/o device
pluggable USB device
display library (gui)
disk image format (mode)
networking module
lowlevel sound module
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.soOn Windows platforms (Cygwin, MinGW/MSYS, MSVC) the output file is a DLL and the name is slightly different:
bx_cmos.dllThe 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
Type | Module name | Source file name | Libtool file name | DLL file name |
---|---|---|---|---|
Display library | sdl2 | sdl2.cc | libbx_sdl2_gui.so | bx_sdl2_gui.dll |
Disk image module | vbox | vbox.cc | libbx_vbox_img.so | bx_vbox_img.dll |
Networking module | slirp | eth_slirp.cc | libbx_eth_slirp.so | bx_eth_slirp.dll |
Lowlevel sound driver | file | soundfile.cc | libbx_soundfile.so | bx_soundfile.dll |
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
Type | Description | Entry function name | Base C++ class used | Remarks |
---|---|---|---|---|
PLUGTYPE_CORE | Core device plugin, always required or depending on core option, highest priority in init order | libmodule_plugin_entry | bx_devmodel_c | Single device only |
PLUGTYPE_STANDARD | Device plugin, mostly required or depending on core plugins, lower priority in init order | libmodule_plugin_entry | bx_devmodel_c | Single device only |
PLUGTYPE_OPTIONAL | Device plugin depending on normal config option | libmodule_plugin_entry | bx_devmodel_c | Network device plugins can create up to 4 instances |
PLUGTYPE_VGA | VGA-compatible device plugin selected with vga: extension=X option | libmodule_plugin_entry | bx_vgacore_c | One plugin of type required / supported |
PLUGTYPE_USB | USB device plugin selected with the portX parameter of the host controller option | libmodule_plugin_entry | usb_device_c | Number of instances not limited |
PLUGTYPE_CI | Config interface plugin selected with the config_interface option | libmodule_gui_plugin_entry | - | One plugin of type required / supported |
PLUGTYPE_GUI | Display library (gui) plugin selected with the display_library option | libmodule_gui_plugin_entry | bx_gui_c | One plugin of type required / supported |
PLUGTYPE_IMG | Additional disk image format selected with the "mode" parameter when setting up a disk image | libmodule_img_plugin_entry | device_image_t | Number of instances not limited |
PLUGTYPE_NET | Networking driver / emulation module selected with the "ethmod" parameter of the NIC options | libeth_module_plugin_entry | eth_pktmover_c | Some modules can create multiple instances |
PLUGTYPE_SND | Sound driver selected with the sound option | libsoundmodule_plugin_entry | bx_sound_lowlevel_c | One plugin of type required / max. 4 different plugins supported |
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:
PLUGIN_PROBE returns the plugin type as defined above (mandatory for all plugins)
PLUGIN_FLAGS returns flags associated with the plugin
PLUGIN_INIT is called after loading the plugin
PLUGIN_FINI is called before unloading the plugin
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 }
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)