594 lines
17 KiB
C++
594 lines
17 KiB
C++
/*
|
|
* PCIDEV: PCI host device mapping
|
|
* Copyright (C) 2003 - Frank Cornelis
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* Based on pcivga code:
|
|
* Copyright (C) 2002,2003 Mike Nordell
|
|
*/
|
|
|
|
// 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 "bochs.h"
|
|
#if BX_PCI_SUPPORT && BX_PCI_DEV_SUPPORT
|
|
|
|
#include "kernel_pcidev.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <signal.h>
|
|
#include <linux/pci.h>
|
|
|
|
#define LOG_THIS thePciDevAdapter->
|
|
|
|
bx_pcidev_c* thePciDevAdapter = 0;
|
|
|
|
int
|
|
libpcidev_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
|
{
|
|
thePciDevAdapter = new bx_pcidev_c ();
|
|
bx_devices.pluginPciDevAdapter = thePciDevAdapter;
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePciDevAdapter, BX_PLUGIN_PCIDEV);
|
|
return 0; // Success
|
|
}
|
|
|
|
void
|
|
libpcidev_LTX_plugin_fini(void)
|
|
{
|
|
}
|
|
|
|
|
|
bx_pcidev_c::bx_pcidev_c(void)
|
|
{
|
|
put("PCI2H");
|
|
settype(PCIDEVLOG);
|
|
}
|
|
|
|
bx_pcidev_c::~bx_pcidev_c(void)
|
|
{
|
|
// nothing for now
|
|
BX_DEBUG(("Exit."));
|
|
}
|
|
|
|
|
|
static void pcidev_sighandler(int param)
|
|
{
|
|
unsigned long irq = ((bx_pcidev_c *)bx_devices.pluginPciDevAdapter)->irq;
|
|
BX_INFO(("Interrupt received."));
|
|
DEV_pic_lower_irq(irq);
|
|
/*
|
|
* We need to first lower the IRQ line or else we don't
|
|
* get any IRQs through
|
|
*/
|
|
DEV_pic_raise_irq(irq);
|
|
}
|
|
|
|
|
|
static bool pcidev_mem_read_handler(unsigned long addr, unsigned long len, void *data, void *param)
|
|
{
|
|
struct region_struct *region = (struct region_struct *)param;
|
|
bx_pcidev_c *pcidev = region->pcidev;
|
|
int fd = pcidev->pcidev_fd;
|
|
if (fd == -1)
|
|
return false; /* we failed to handle the request, so let a default handler do it for us */
|
|
|
|
BX_INFO(("Reading I/O memory at %#x", addr));
|
|
struct pcidev_io_struct io;
|
|
io.address = addr + region->host_start - region->start;
|
|
int ret;
|
|
switch(len) {
|
|
case 1:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_MEM_BYTE, &io);
|
|
*(unsigned char *)data = io.value;
|
|
break;
|
|
case 2:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_MEM_WORD, &io);
|
|
*(unsigned short *)data = io.value;
|
|
break;
|
|
case 4:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_MEM_DWORD, &io);
|
|
*(unsigned long *)data = io.value;
|
|
break;
|
|
default:
|
|
BX_ERROR(("Unsupported pcidev read mem operation"));
|
|
break;
|
|
}
|
|
if (ret == -1) {
|
|
BX_ERROR(("pcidev read mem error"));
|
|
}
|
|
return true; // ok, we handled the request
|
|
}
|
|
|
|
|
|
static bool pcidev_mem_write_handler(unsigned long addr, unsigned long len, void *data, void *param)
|
|
{
|
|
struct region_struct *region = (struct region_struct *)param;
|
|
bx_pcidev_c *pcidev = region->pcidev;
|
|
int fd = pcidev->pcidev_fd;
|
|
if (fd == -1)
|
|
return false; /* we failed to handle the request, so let a default handler do it for us */
|
|
|
|
BX_INFO(("Writing I/O memory at %#x", addr));
|
|
struct pcidev_io_struct io;
|
|
io.address = addr + region->host_start - region->start;
|
|
int ret;
|
|
switch(len) {
|
|
case 1:
|
|
io.value = *(unsigned char *)data;
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_MEM_BYTE, &io);
|
|
break;
|
|
case 2:
|
|
io.value = *(unsigned short *)data;
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_MEM_WORD, &io);
|
|
break;
|
|
case 4:
|
|
io.value = *(unsigned long *)data;
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_MEM_DWORD, &io);
|
|
break;
|
|
default:
|
|
BX_ERROR(("Unsupported pcidev write mem operation"));
|
|
break;
|
|
}
|
|
if (ret == -1) {
|
|
BX_ERROR(("pcidev write mem error"));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static const char * const pcidev_name = "Experimental PCI 2 host PCI";
|
|
|
|
void
|
|
bx_pcidev_c::init(void)
|
|
{
|
|
// called once when bochs initializes
|
|
BX_PCIDEV_THIS pcidev_fd = -1;
|
|
int fd;
|
|
fd = open("/dev/pcidev", O_RDWR);
|
|
if (fd == -1) {
|
|
switch(errno) {
|
|
case ENODEV:
|
|
BX_ERROR(("The pcidev kernel module is not loaded!"));
|
|
break;
|
|
default:
|
|
perror("open /dev/pcidev");
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
BX_PCIDEV_THIS pcidev_fd = fd;
|
|
struct pcidev_find_struct find;
|
|
unsigned short vendor = bx_options.pcidev.Ovendor->get();
|
|
unsigned short device = bx_options.pcidev.Odevice->get();
|
|
find.deviceID = device;
|
|
find.vendorID = vendor;
|
|
if (ioctl(fd, PCIDEV_IOCTL_FIND, &find) == -1) {
|
|
switch (errno) {
|
|
case ENOENT:
|
|
BX_ERROR(("PCI device not found on host system."));
|
|
break;
|
|
case EBUSY:
|
|
BX_ERROR(("PCI device already used by another kernel module."));
|
|
break;
|
|
default:
|
|
perror("ioctl");
|
|
break;
|
|
}
|
|
close(fd);
|
|
BX_PCIDEV_THIS pcidev_fd = -1;
|
|
return;
|
|
}
|
|
BX_INFO(("vendor: %x; device: %x @ host %x:%x.%d", vendor, device,
|
|
find.bus, find.device, find.func));
|
|
|
|
DEV_register_pci_handlers(this,
|
|
pci_read_handler,
|
|
pci_write_handler,
|
|
DEV_find_free_devfunc(),
|
|
pcidev_name);
|
|
|
|
BX_PCIDEV_THIS irq = PCIDEV_IRQ; // initial irq value
|
|
/*
|
|
* Next function just checks against IRQ sharing... for now Bochs
|
|
* does not allow this... should be changed... IRQ sharing for PCI
|
|
* devices is quite common
|
|
*/
|
|
DEV_register_irq(BX_PCIDEV_THIS irq, pcidev_name);
|
|
|
|
for (int idx = 0; idx < PCIDEV_COUNT_RESOURCES; idx++) {
|
|
if (!find.resources[idx].start)
|
|
continue;
|
|
BX_INFO(("PCI resource @ %x-%x (%s)", find.resources[idx].start,
|
|
find.resources[idx].end,
|
|
(find.resources[idx].flags & PCIDEV_RESOURCE_IO ? "I/O" : "Mem")));
|
|
BX_PCIDEV_THIS regions[idx].start = find.resources[idx].start;
|
|
BX_PCIDEV_THIS regions[idx].end = find.resources[idx].end;
|
|
BX_PCIDEV_THIS regions[idx].host_start = find.resources[idx].start; // we start with an identical mapping
|
|
struct pcidev_io_struct io;
|
|
io.address = PCI_BASE_ADDRESS_0 + idx * 4;
|
|
if (ioctl(fd, PCIDEV_IOCTL_READ_CONFIG_DWORD, &io) == -1)
|
|
BX_ERROR(("Error reading a base address config reg."));
|
|
BX_PCIDEV_THIS regions[idx].config_value = io.value;
|
|
/*
|
|
* We will use ®ion[idx] as parameter for our I/O or memory
|
|
* handler. So we provide a pcidev pointer to the pcidev object
|
|
* in order for the handle to be able to use its pcidev object
|
|
*/
|
|
BX_PCIDEV_THIS regions[idx].pcidev = this;
|
|
unsigned long flags = find.resources[idx].flags;
|
|
if (flags & PCIDEV_RESOURCE_IO) {
|
|
BX_INFO(("Registering I/O port handler for %#x to %#x", find.resources[idx].start,
|
|
find.resources[idx].end));
|
|
if (!DEV_register_ioread_handler_range(&(BX_PCIDEV_THIS regions[idx]),
|
|
read_handler, find.resources[idx].start,
|
|
find.resources[idx].end, "pcidev", 7))
|
|
BX_ERROR(("Could not register I/O port read handler range %#x to %#x",
|
|
find.resources[idx].start, find.resources[idx].end));
|
|
if (!DEV_register_iowrite_handler_range(&(BX_PCIDEV_THIS regions[idx]),
|
|
write_handler, find.resources[idx].start,
|
|
find.resources[idx].end, "pcidev", 7))
|
|
BX_ERROR(("Could not register I/O port write handler range %#x to %#x",
|
|
find.resources[idx].start, find.resources[idx].end));
|
|
}
|
|
else if (flags & PCIDEV_RESOURCE_MEM) {
|
|
BX_INFO(("Registering memory I/O handler for %#x to %#x", find.resources[idx].start,
|
|
find.resources[idx].end));
|
|
DEV_register_memory_handlers(pcidev_mem_read_handler,
|
|
&(BX_PCIDEV_THIS regions[idx]),
|
|
pcidev_mem_write_handler,
|
|
&(BX_PCIDEV_THIS regions[idx]),
|
|
find.resources[idx].start,
|
|
find.resources[idx].end);
|
|
}
|
|
}
|
|
|
|
struct sigaction sa;
|
|
sa.sa_handler = pcidev_sighandler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sigaction(SIGUSR1, &sa, NULL);
|
|
|
|
/*
|
|
* The kernel pcidev will fire SIGUSR1 signals when it receives
|
|
* interrupts from the host PCI device.
|
|
*/
|
|
ioctl(fd, PCIDEV_IOCTL_INTERRUPT, 1);
|
|
|
|
/*
|
|
* Let the kernel fire some fake IRQ signals
|
|
*/
|
|
//ioctl(fd, PCIDEV_IOCTL_INTERRUPT_TEST, 1);
|
|
}
|
|
|
|
void
|
|
bx_pcidev_c::reset(unsigned type)
|
|
{
|
|
}
|
|
|
|
|
|
// static pci configuration space read callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
Bit32u
|
|
bx_pcidev_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
|
|
{
|
|
#if !BX_USE_PCIDEV_SMF
|
|
bx_pcidev_c *class_ptr = (bx_pcidev_c *) this_ptr;
|
|
|
|
return class_ptr->pci_read(address, io_len);
|
|
}
|
|
|
|
|
|
Bit32u
|
|
bx_pcidev_c::pci_read(Bit8u address, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_PCIDEV_SMF
|
|
|
|
if (io_len > 4 || io_len == 0) {
|
|
BX_DEBUG(("Experimental PCIDEV read register 0x%02x, len=%u !",
|
|
(unsigned) address, (unsigned) io_len));
|
|
return 0xffffffff;
|
|
}
|
|
|
|
int fd = BX_PCIDEV_THIS pcidev_fd;
|
|
if (fd == -1)
|
|
return 0xffffffff;
|
|
|
|
struct pcidev_io_struct io;
|
|
int ret;
|
|
io.address = address;
|
|
switch(io_len) {
|
|
case 1:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_CONFIG_BYTE, &io);
|
|
break;
|
|
case 2:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_CONFIG_WORD, &io);
|
|
break;
|
|
case 4:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_CONFIG_DWORD, &io);
|
|
break;
|
|
}
|
|
if (ret == -1)
|
|
BX_ERROR(("pcidev config read error"));
|
|
|
|
// we don't use the host irq line but our own bochs irq line
|
|
if (address == PCI_INTERRUPT_LINE) {
|
|
io.value = (io.value & 0xffffff00) | (BX_PCIDEV_THIS irq & 0xff);
|
|
}
|
|
if (PCI_BASE_ADDRESS_0 <= address && address <= PCI_BASE_ADDRESS_5) {
|
|
BX_INFO(("Reading pcidev base address %d.",
|
|
(address - PCI_BASE_ADDRESS_0) / 4));
|
|
if (address & 3)
|
|
BX_ERROR(("base address not aligned!"));
|
|
io.value = BX_PCIDEV_THIS regions[(address - PCI_BASE_ADDRESS_0) >> 2].config_value;
|
|
}
|
|
|
|
return io.value;
|
|
}
|
|
|
|
|
|
// static pci configuration space write callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
void
|
|
bx_pcidev_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#if !BX_USE_PCIDEV_SMF
|
|
bx_pcidev_c *class_ptr = (bx_pcidev_c *) this_ptr;
|
|
|
|
class_ptr->pci_write(address, value, io_len);
|
|
}
|
|
|
|
void
|
|
bx_pcidev_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_PCIDEV_SMF
|
|
|
|
if (io_len > 4 || io_len == 0) {
|
|
BX_DEBUG(("Experimental PCIDEV write register 0x%02x, len=%u !",
|
|
(unsigned) address, (unsigned) io_len));
|
|
return;
|
|
}
|
|
|
|
int fd = BX_PCIDEV_THIS pcidev_fd;
|
|
if (fd == -1)
|
|
return;
|
|
|
|
// we do a host 2 guest irq line mapping
|
|
if (address == PCI_INTERRUPT_LINE) {
|
|
value &= 0xff;
|
|
BX_INFO(("Changing the pcidev irq line from %d to %d",
|
|
BX_PCIDEV_THIS irq, value));
|
|
if (BX_PCIDEV_THIS irq)
|
|
DEV_unregister_irq(BX_PCIDEV_THIS irq, pcidev_name);
|
|
BX_PCIDEV_THIS irq = value;
|
|
if (BX_PCIDEV_THIS irq) // win98 likes to set irq = 0 at power off
|
|
DEV_register_irq(BX_PCIDEV_THIS irq, pcidev_name);
|
|
return;
|
|
}
|
|
if (PCI_BASE_ADDRESS_0 <= address && address <= PCI_BASE_ADDRESS_5) {
|
|
/*
|
|
* Two things to do here:
|
|
* - update the cached config space value via a probe
|
|
* - remap the I/O or memory handler if required
|
|
*/
|
|
BX_INFO(("Changing pcidev base address %d. New value: %#x",
|
|
(address - PCI_BASE_ADDRESS_0) / 4, value));
|
|
if (address & 3) {
|
|
BX_ERROR(("base address not aligned!"));
|
|
return;
|
|
}
|
|
int io_reg_idx = (address - PCI_BASE_ADDRESS_0) >> 2;
|
|
struct pcidev_io_struct io;
|
|
int ret;
|
|
io.address = address;
|
|
io.value = value;
|
|
ret = ioctl(fd, PCIDEV_IOCTL_PROBE_CONFIG_DWORD, &io);
|
|
if (ret == -1) {
|
|
BX_ERROR(("Error probing a base address reg!"));
|
|
return;
|
|
}
|
|
unsigned long base = io.value;
|
|
BX_PCIDEV_THIS regions[io_reg_idx].config_value = base;
|
|
/* remap the I/O or memory handler if required using io.value
|
|
* We assume that an I/O memory region will stay and I/O memory
|
|
* region. And that an I/O port region also will stay an I/O port
|
|
* region.
|
|
*/
|
|
if ((base & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
|
|
BX_INFO(("Remapping memory region from %#x to %#x",
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
base & PCI_BASE_ADDRESS_MEM_MASK));
|
|
if (BX_PCIDEV_THIS regions[io_reg_idx].start && // dirty hack
|
|
!DEV_unregister_memory_handlers(pcidev_mem_read_handler,
|
|
pcidev_mem_write_handler,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end))
|
|
BX_ERROR(("Error while unregistering old memory handlers!"));
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end =
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end +
|
|
(base & PCI_BASE_ADDRESS_MEM_MASK) -
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start;
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start = base & PCI_BASE_ADDRESS_MEM_MASK;
|
|
if (BX_PCIDEV_THIS regions[io_reg_idx].start) // dirty hack
|
|
DEV_register_memory_handlers(pcidev_mem_read_handler,
|
|
&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
pcidev_mem_write_handler,
|
|
&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end);
|
|
}
|
|
else {
|
|
/*
|
|
* Remap our I/O port handlers here.
|
|
*/
|
|
BX_INFO(("Remapping I/O port region from %#x to %#x",
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
base & PCI_BASE_ADDRESS_IO_MASK));
|
|
if (BX_PCIDEV_THIS regions[io_reg_idx].start) { // dirty hack
|
|
if (!DEV_unregister_ioread_handler_range(&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
read_handler,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end,
|
|
7))
|
|
BX_ERROR(("Error while unregistering old I/O port read handlers!"));
|
|
if (!DEV_unregister_iowrite_handler_range(&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
write_handler,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end,
|
|
7))
|
|
BX_ERROR(("Error while unregistering old I/O port write handlers!"));
|
|
}
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end =
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end +
|
|
(base & PCI_BASE_ADDRESS_IO_MASK) -
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start;
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start = base & PCI_BASE_ADDRESS_IO_MASK;
|
|
if (BX_PCIDEV_THIS regions[io_reg_idx].start) { // dirty hack
|
|
DEV_register_ioread_handler_range(
|
|
&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
read_handler,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end,
|
|
"pcidev", 7);
|
|
DEV_register_iowrite_handler_range(
|
|
&(BX_PCIDEV_THIS regions[io_reg_idx]),
|
|
write_handler,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].start,
|
|
BX_PCIDEV_THIS regions[io_reg_idx].end,
|
|
"pcidev", 7);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
struct pcidev_io_struct io;
|
|
int ret;
|
|
io.address = address;
|
|
io.value = value;
|
|
|
|
switch(io_len) {
|
|
case 1:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_CONFIG_BYTE, &io);
|
|
break;
|
|
case 2:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_CONFIG_WORD, &io);
|
|
break;
|
|
case 4:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_CONFIG_DWORD, &io);
|
|
break;
|
|
}
|
|
if (ret == -1)
|
|
BX_ERROR(("pcidev config write error"));
|
|
}
|
|
|
|
|
|
Bit32u
|
|
bx_pcidev_c::read_handler(void *param, Bit32u address, unsigned io_len)
|
|
{
|
|
#if !BX_USE_PCIDEV_SMF
|
|
bx_pcidev_c *class_ptr = ((struct region_struct *)param)->pcidev;
|
|
|
|
return class_ptr->read(param, address, io_len);
|
|
}
|
|
|
|
Bit32u
|
|
bx_pcidev_c::read(void *param, Bit32u address, unsigned io_len)
|
|
{
|
|
#endif // !BX_USE_PCIDEV_SMF
|
|
|
|
struct region_struct *region = (struct region_struct *)param;
|
|
Bit32u value;
|
|
|
|
int fd = BX_PCIDEV_THIS pcidev_fd;
|
|
if (fd == -1)
|
|
return 0xffffffff;
|
|
|
|
struct pcidev_io_struct io;
|
|
// here we map the io address
|
|
io.address = address + region->host_start - region->start;
|
|
int ret;
|
|
switch(io_len) {
|
|
case 1:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_IO_BYTE, &io);
|
|
break;
|
|
case 2:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_IO_WORD, &io);
|
|
break;
|
|
case 4:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_READ_IO_DWORD, &io);
|
|
break;
|
|
}
|
|
if (ret == -1) {
|
|
BX_ERROR(("pcidev read I/O error"));
|
|
io.value = 0xffffffff;
|
|
}
|
|
|
|
return io.value;
|
|
}
|
|
|
|
void
|
|
bx_pcidev_c::write_handler(void *param, Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#if !BX_USE_PCIDEV_SMF
|
|
bx_pcidev_c *class_ptr = ((struct region_struct *)param)->pcidev;
|
|
|
|
class_ptr->write(param, address, value, io_len);
|
|
}
|
|
|
|
void
|
|
bx_pcidev_c::write(void *param, Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#else
|
|
//UNUSED(this_ptr);
|
|
#endif // !BX_USE_PCIDEV_SMF
|
|
|
|
struct region_struct *region = (struct region_struct *)param;
|
|
|
|
int fd = BX_PCIDEV_THIS pcidev_fd;
|
|
if (fd == -1)
|
|
return;
|
|
|
|
struct pcidev_io_struct io;
|
|
int ret;
|
|
// here we map the I/O address
|
|
io.address = address + region->host_start - region->start;
|
|
io.value = value;
|
|
|
|
switch(io_len) {
|
|
case 1:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_IO_BYTE, &io);
|
|
break;
|
|
case 2:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_IO_WORD, &io);
|
|
break;
|
|
case 4:
|
|
ret = ioctl(fd, PCIDEV_IOCTL_WRITE_IO_DWORD, &io);
|
|
break;
|
|
}
|
|
if (ret == -1)
|
|
BX_ERROR(("pcidev I/O write error"));
|
|
}
|
|
|
|
#endif // BX_PCI_SUPPORT && BX_PCI_DEV_SUPPORT
|