555 lines
16 KiB
C++
555 lines
16 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id: plugin.cc,v 1.7 2003-07-10 20:26:05 vruppert Exp $
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This file defines the plugin and plugin-device registration functions and
|
|
// the device registration functions. It handles dynamic loading of modules,
|
|
// using the LTDL library for cross-platform support.
|
|
//
|
|
// This file is based on the plugin.c file from plex86, but with significant
|
|
// changes to make it work in Bochs.
|
|
// Plex86 is Copyright (C) 1999-2000 The plex86 developers team
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "bochs.h"
|
|
#include "plugin.h"
|
|
|
|
#define LOG_THIS genlog->
|
|
|
|
#define PLUGIN_INIT_FMT_STRING "lib%s_LTX_plugin_init"
|
|
#define PLUGIN_FINI_FMT_STRING "lib%s_LTX_plugin_fini"
|
|
#define PLUGIN_PATH ""
|
|
|
|
#ifndef WIN32
|
|
#define PLUGIN_FILENAME_FORMAT "libbx_%s.la"
|
|
#else
|
|
#define PLUGIN_FILENAME_FORMAT "bx_%s.dll"
|
|
#endif
|
|
|
|
|
|
|
|
void (*pluginRegisterIRQ)(unsigned irq, const char* name) = 0;
|
|
void (*pluginUnregisterIRQ)(unsigned irq, const char* name) = 0;
|
|
|
|
void (* pluginResetSignal)(unsigned sig) = 0;
|
|
|
|
void (*pluginSetHRQ)(unsigned val) = 0;
|
|
void (*pluginSetHRQHackCallback)( void (*callback)(void) ) = 0;
|
|
|
|
int (*pluginRegisterIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
|
|
unsigned base, const char *name, unsigned len) = 0;
|
|
int (*pluginRegisterIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
|
|
unsigned base, const char *name, unsigned len) = 0;
|
|
int (*pluginRegisterDefaultIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
|
|
const char *name, unsigned len) = 0;
|
|
int (*pluginRegisterDefaultIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
|
|
const char *name, unsigned len) = 0;
|
|
int (*pluginRegisterTimer)(void *this_ptr, void (*funct)(void *),
|
|
Bit32u useconds, bx_bool continuous,
|
|
bx_bool active, const char* name) = 0;
|
|
void (*pluginActivateTimer)(unsigned id, Bit32u usec, bx_bool continuous) = 0;
|
|
|
|
void (*pluginHRQHackCallback)(void);
|
|
unsigned pluginHRQ = 0;
|
|
|
|
plugin_t *plugins = NULL; /* Head of the linked list of plugins */
|
|
#if BX_PLUGINS
|
|
static void plugin_init_one(plugin_t *plugin);
|
|
#endif
|
|
|
|
device_t *devices = NULL; /* Head of the linked list of registered devices */
|
|
|
|
plugin_t *current_plugin_context = NULL;
|
|
|
|
/************************************************************************/
|
|
/* Builtins declarations */
|
|
/************************************************************************/
|
|
|
|
static void
|
|
builtinRegisterIRQ(unsigned irq, const char* name)
|
|
{
|
|
#if 0
|
|
pluginlog->panic("builtinRegisterIRQ called, no pic plugin loaded?");
|
|
#else
|
|
bx_devices.register_irq(irq, name);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
builtinUnregisterIRQ(unsigned irq, const char* name)
|
|
{
|
|
#if 0
|
|
pluginlog->panic("builtinUnregisterIRQ called, no pic plugin loaded?");
|
|
#else
|
|
bx_devices.unregister_irq(irq, name);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
builtinSetHRQ(unsigned val)
|
|
{
|
|
#if 0
|
|
pluginlog->panic("builtinSetHRQ called, no plugin loaded?");
|
|
#else
|
|
pluginHRQ = val;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
builtinSetHRQHackCallback( void (*callback)(void) )
|
|
{
|
|
#if 0
|
|
pluginlog->panic("builtinSetHRQHackCallback called, no plugin loaded?");
|
|
#else
|
|
pluginHRQHackCallback = callback;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
builtinResetSignal( unsigned )
|
|
{
|
|
pluginlog->panic("builtinResetSignal called, no plugin loaded?");
|
|
}
|
|
|
|
static int
|
|
builtinRegisterIOReadHandler(void *thisPtr, ioReadHandler_t callback,
|
|
unsigned base, const char *name, unsigned len)
|
|
{
|
|
BX_ASSERT (len<8);
|
|
bx_devices.register_io_read_handler (thisPtr, callback, base, name);
|
|
pluginlog->ldebug("plugin %s registered I/O read address at %04x", name, base);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
builtinRegisterIOWriteHandler(void *thisPtr, ioWriteHandler_t callback,
|
|
unsigned base, const char *name, unsigned len)
|
|
{
|
|
BX_ASSERT (len<8);
|
|
bx_devices.register_io_write_handler (thisPtr, callback, base, name);
|
|
pluginlog->ldebug("plugin %s registered I/O write address at %04x", name, base);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
builtinRegisterDefaultIOReadHandler(void *thisPtr, ioReadHandler_t callback,
|
|
const char *name, unsigned len)
|
|
{
|
|
BX_ASSERT (len<8);
|
|
bx_devices.register_default_io_read_handler (thisPtr, callback, name);
|
|
pluginlog->ldebug("plugin %s registered default I/O read ", name);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
builtinRegisterDefaultIOWriteHandler(void *thisPtr, ioWriteHandler_t callback,
|
|
const char *name, unsigned len)
|
|
{
|
|
BX_ASSERT (len<8);
|
|
bx_devices.register_default_io_write_handler (thisPtr, callback, name);
|
|
pluginlog->ldebug("plugin %s registered default I/O write ", name);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
builtinRegisterTimer(void *this_ptr, void (*funct)(void *),
|
|
Bit32u useconds, bx_bool continuous,
|
|
bx_bool active, const char* name)
|
|
{
|
|
int id = bx_pc_system.register_timer (this_ptr, funct, useconds, continuous, active, name);
|
|
pluginlog->ldebug("plugin %s registered timer %d", name, id);
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
builtinActivateTimer(unsigned id, Bit32u usec, bx_bool continuous)
|
|
{
|
|
bx_pc_system.activate_timer (id, usec, continuous);
|
|
pluginlog->ldebug("plugin activated timer %d", id);
|
|
}
|
|
|
|
#if BX_PLUGINS
|
|
/************************************************************************/
|
|
/* Plugin initialization / deinitialization */
|
|
/************************************************************************/
|
|
|
|
void
|
|
plugin_init_all (void)
|
|
{
|
|
plugin_t *plugin;
|
|
|
|
pluginlog->info("Initializing plugins");
|
|
|
|
for (plugin = plugins; plugin; plugin = plugin->next)
|
|
{
|
|
char *arg_ptr = plugin->args;
|
|
|
|
/* process the command line */
|
|
plugin->argc = 0;
|
|
while (plugin->argc < MAX_ARGC)
|
|
{
|
|
while (*arg_ptr && isspace (*arg_ptr))
|
|
arg_ptr++;
|
|
|
|
if (!*arg_ptr)
|
|
break;
|
|
plugin->argv[plugin->argc++] = arg_ptr;
|
|
|
|
while (*arg_ptr && !isspace (*arg_ptr))
|
|
arg_ptr++;
|
|
|
|
if (!*arg_ptr)
|
|
break;
|
|
*arg_ptr++ = '\0';
|
|
}
|
|
|
|
/* initialize the plugin */
|
|
if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv))
|
|
{
|
|
pluginlog->panic("Plugin initialization failed for %s", plugin->name);
|
|
plugin_abort();
|
|
}
|
|
|
|
plugin->initialized = 1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
plugin_init_one(plugin_t *plugin)
|
|
{
|
|
char *arg_ptr = plugin->args;
|
|
|
|
/* process the command line */
|
|
plugin->argc = 0;
|
|
while (plugin->argc < MAX_ARGC)
|
|
{
|
|
while (*arg_ptr && isspace (*arg_ptr))
|
|
arg_ptr++;
|
|
|
|
if (!*arg_ptr)
|
|
break;
|
|
plugin->argv[plugin->argc++] = arg_ptr;
|
|
|
|
while (*arg_ptr && !isspace (*arg_ptr))
|
|
arg_ptr++;
|
|
|
|
if (!*arg_ptr)
|
|
break;
|
|
*arg_ptr++ = '\0';
|
|
}
|
|
|
|
/* initialize the plugin */
|
|
if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv))
|
|
{
|
|
pluginlog->info("Plugin initialization failed for %s", plugin->name);
|
|
plugin_abort();
|
|
}
|
|
|
|
plugin->initialized = 1;
|
|
}
|
|
|
|
|
|
plugin_t *
|
|
plugin_unload(plugin_t *plugin)
|
|
{
|
|
plugin_t *dead_plug;
|
|
|
|
if (plugin->initialized)
|
|
plugin->plugin_fini ();
|
|
|
|
lt_dlclose (plugin->handle);
|
|
free (plugin->name);
|
|
free (plugin->args);
|
|
|
|
dead_plug = plugin;
|
|
plugin = plugin->next;
|
|
free (dead_plug);
|
|
|
|
return plugin;
|
|
}
|
|
|
|
|
|
void
|
|
plugin_fini_all (void)
|
|
{
|
|
plugin_t *plugin;
|
|
|
|
for (plugin = plugins; plugin; plugin = plugin_unload (plugin));
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
plugin_load (char *name, char *args, plugintype_t type)
|
|
{
|
|
plugin_t *plugin;
|
|
|
|
plugin = (plugin_t *)malloc (sizeof (plugin_t));
|
|
if (!plugin)
|
|
{
|
|
BX_PANIC (("malloc plugin_t failed"));
|
|
}
|
|
|
|
plugin->type = type;
|
|
plugin->name = name;
|
|
plugin->args = args;
|
|
plugin->initialized = 0;
|
|
|
|
char plugin_filename[BX_PATHNAME_LEN], buf[BX_PATHNAME_LEN];
|
|
sprintf (buf, PLUGIN_FILENAME_FORMAT, name);
|
|
sprintf(plugin_filename, "%s%s", PLUGIN_PATH, buf);
|
|
|
|
// Set context so that any devices that the plugin registers will
|
|
// be able to see which plugin created them. The registration will
|
|
// be called from either dlopen (global constructors) or plugin_init.
|
|
BX_ASSERT (current_plugin_context == NULL);
|
|
current_plugin_context = plugin;
|
|
plugin->handle = lt_dlopen (plugin_filename);
|
|
BX_INFO (("lt_dlhandle is %p", plugin->handle));
|
|
if (!plugin->handle)
|
|
{
|
|
current_plugin_context = NULL;
|
|
BX_PANIC (("dlopen failed for module '%s': %s", name, lt_dlerror ()));
|
|
free (plugin);
|
|
return;
|
|
}
|
|
|
|
sprintf (buf, PLUGIN_INIT_FMT_STRING, name);
|
|
plugin->plugin_init =
|
|
(int (*)(struct _plugin_t *, enum plugintype_t, int, char *[])) /* monster typecast */
|
|
lt_dlsym (plugin->handle, buf);
|
|
if (plugin->plugin_init == NULL) {
|
|
pluginlog->panic("could not find plugin_init: %s", lt_dlerror ());
|
|
plugin_abort ();
|
|
}
|
|
|
|
sprintf (buf, PLUGIN_FINI_FMT_STRING, name);
|
|
plugin->plugin_fini = (void (*)(void)) lt_dlsym (plugin->handle, buf);
|
|
if (plugin->plugin_init == NULL) {
|
|
pluginlog->panic("could not find plugin_fini: %s", lt_dlerror ());
|
|
plugin_abort ();
|
|
}
|
|
pluginlog->info("loaded plugin %s",plugin_filename);
|
|
|
|
|
|
/* Insert plugin at the _end_ of the plugin linked list. */
|
|
plugin->next = NULL;
|
|
|
|
if (!plugins)
|
|
{
|
|
/* Empty list, this become the first entry. */
|
|
plugins = plugin;
|
|
}
|
|
else
|
|
{
|
|
/* Non-empty list. Add to end. */
|
|
plugin_t *temp = plugins;
|
|
|
|
while (temp->next)
|
|
temp = temp->next;
|
|
|
|
temp->next = plugin;
|
|
}
|
|
|
|
plugin_init_one(plugin);
|
|
|
|
// check that context didn't change. This should only happen if we
|
|
// need a reentrant plugin_load.
|
|
BX_ASSERT (current_plugin_context == plugin);
|
|
current_plugin_context = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
plugin_abort (void)
|
|
{
|
|
pluginlog->panic("plugin load aborted");
|
|
}
|
|
|
|
#endif /* end of #if BX_PLUGINS */
|
|
|
|
/************************************************************************/
|
|
/* Plugin system: initialisation of plugins entry points */
|
|
/************************************************************************/
|
|
|
|
void
|
|
plugin_startup(void)
|
|
{
|
|
pluginRegisterIRQ = builtinRegisterIRQ;
|
|
pluginUnregisterIRQ = builtinUnregisterIRQ;
|
|
|
|
pluginResetSignal = builtinResetSignal;
|
|
|
|
pluginSetHRQHackCallback = builtinSetHRQHackCallback;
|
|
pluginSetHRQ = builtinSetHRQ;
|
|
|
|
pluginRegisterIOReadHandler = builtinRegisterIOReadHandler;
|
|
pluginRegisterIOWriteHandler = builtinRegisterIOWriteHandler;
|
|
|
|
pluginRegisterDefaultIOReadHandler = builtinRegisterDefaultIOReadHandler;
|
|
pluginRegisterDefaultIOWriteHandler = builtinRegisterDefaultIOWriteHandler;
|
|
|
|
pluginRegisterTimer = builtinRegisterTimer;
|
|
pluginActivateTimer = builtinActivateTimer;
|
|
|
|
#if BX_PLUGINS
|
|
pluginlog = new logfunctions();
|
|
pluginlog->put("PLGIN");
|
|
pluginlog->settype(PLUGINLOG);
|
|
int status = lt_dlinit ();
|
|
if (status != 0) {
|
|
BX_ERROR (("initialization error in ltdl library (for loading plugins)"));
|
|
BX_PANIC (("error message was: %s", lt_dlerror ()));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Plugin system: Device registration */
|
|
/************************************************************************/
|
|
|
|
void pluginRegisterDeviceDevmodel(plugin_t *plugin, plugintype_t type, bx_devmodel_c *devmodel, char *name)
|
|
{
|
|
device_t *device;
|
|
|
|
device = (device_t *)malloc (sizeof (device_t));
|
|
if (!device)
|
|
{
|
|
pluginlog->panic("can't allocate device_t");
|
|
}
|
|
|
|
device->name = name;
|
|
BX_ASSERT (devmodel != NULL);
|
|
device->devmodel = devmodel;
|
|
device->plugin = plugin; // this can be NULL
|
|
device->use_devmodel_interface = 1;
|
|
device->device_init_mem = NULL; // maybe should use 1 to detect any use?
|
|
device->device_init_dev = NULL;
|
|
device->device_reset = NULL;
|
|
device->device_load_state = NULL;
|
|
device->device_save_state = NULL;
|
|
device->next = NULL;
|
|
|
|
// Don't add every kind of device to the list.
|
|
switch (type) {
|
|
case PLUGTYPE_CORE:
|
|
// Core devices are present whether or not we are using plugins, so
|
|
// they are managed by the same code in iodev/devices.cc whether
|
|
// plugins are on or off.
|
|
return; // Do not add core devices to the devices list.
|
|
case PLUGTYPE_OPTIONAL:
|
|
case PLUGTYPE_USER:
|
|
default:
|
|
// The plugin system will manage optional and user devices only.
|
|
break;
|
|
}
|
|
|
|
if (!devices)
|
|
{
|
|
/* Empty list, this become the first entry. */
|
|
devices = device;
|
|
}
|
|
else
|
|
{
|
|
/* Non-empty list. Add to end. */
|
|
device_t *temp = devices;
|
|
|
|
while (temp->next)
|
|
temp = temp->next;
|
|
|
|
temp->next = device;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Plugin system: Check if a plugin is loaded */
|
|
/************************************************************************/
|
|
|
|
bx_bool pluginDevicePresent(char *name)
|
|
{
|
|
device_t *device;
|
|
|
|
for (device = devices; device; device = device->next)
|
|
{
|
|
if (strcmp(device->name,name)==0) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if BX_PLUGINS
|
|
/************************************************************************/
|
|
/* Plugin system: Load one plugin */
|
|
/************************************************************************/
|
|
|
|
int bx_load_plugin (const char *name, plugintype_t type)
|
|
{
|
|
char *namecopy = new char[1+strlen(name)];
|
|
strcpy (namecopy, name);
|
|
plugin_load (namecopy, "", type);
|
|
return 0;
|
|
}
|
|
#endif /* end of #if BX_PLUGINS */
|
|
|
|
/*************************************************************************/
|
|
/* Plugin system: Execute init function of all registered plugin-devices */
|
|
/*************************************************************************/
|
|
|
|
void bx_init_plugins()
|
|
{
|
|
device_t *device;
|
|
|
|
// two loops
|
|
for (device = devices; device; device = device->next)
|
|
{
|
|
if (!device->use_devmodel_interface) {
|
|
if (device->device_init_mem != NULL) {
|
|
pluginlog->info("init_mem of '%s' plugin device by function pointer",device->name);
|
|
device->device_init_mem(BX_MEM(0));
|
|
}
|
|
} else {
|
|
pluginlog->info("init_mem of '%s' plugin device by virtual method",device->name);
|
|
device->devmodel->init_mem (BX_MEM(0));
|
|
}
|
|
}
|
|
|
|
for (device = devices; device; device = device->next)
|
|
{
|
|
if (!device->use_devmodel_interface) {
|
|
if (device->device_init_dev != NULL) {
|
|
pluginlog->info("init_dev of '%s' plugin device by function pointer",device->name);
|
|
device->device_init_dev();
|
|
}
|
|
} else {
|
|
pluginlog->info("init_dev of '%s' plugin device by virtual method",device->name);
|
|
device->devmodel->init ();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/* Plugin system: Execute reset function of all registered plugin-devices */
|
|
/**************************************************************************/
|
|
|
|
void bx_reset_plugins(unsigned signal)
|
|
{
|
|
device_t *device;
|
|
for (device = devices; device; device = device->next)
|
|
{
|
|
if (!device->use_devmodel_interface) {
|
|
if (device->device_reset != NULL) {
|
|
pluginlog->info("reset of '%s' plugin device by function pointer",device->name);
|
|
device->device_reset(signal);
|
|
}
|
|
} else {
|
|
pluginlog->info("reset of '%s' plugin device by virtual method",device->name);
|
|
device->devmodel->reset (signal);
|
|
}
|
|
}
|
|
}
|