/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator.pci;
import org.jpc.emulator.*;
import java.io.*;
/**
* Provides a default implementations for the core features of a standard PCI
* device. This includes assignment of device/function numbers, handling of
* interrupts and implementation of the PCI configuration space for this device.
* @author Chris Dennis
*/
public abstract class AbstractPCIDevice extends AbstractHardwareComponent implements PCIDevice
{
private int deviceFunctionNumber;
private byte[] configuration;
private int irq;
private IRQBouncer irqBouncer;
private boolean pciRegistered;
public AbstractPCIDevice()
{
pciRegistered = false;
configuration = new byte[256];
}
public void saveState(DataOutput output) throws IOException
{
output.writeInt(irq);
output.writeInt(deviceFunctionNumber);
output.writeInt(configuration.length);
output.write(configuration);
}
public void loadState(DataInput input) throws IOException
{
irq = input.readInt();
deviceFunctionNumber = input.readInt();
int len = input.readInt();
configuration = new byte[len];
input.readFully(configuration, 0, len);
}
//PCI Bus Registering
public int getDeviceFunctionNumber()
{
return deviceFunctionNumber;
}
public void assignDeviceFunctionNumber(int devFN)
{
deviceFunctionNumber = devFN;
}
public boolean autoAssignDeviceFunctionNumber()
{
return true;
}
public void deassignDeviceFunctionNumber()
{
pciRegistered = false;
assignDeviceFunctionNumber(-1);
}
private boolean checkConfigWrite(int address)
{
switch (0xff & configReadByte(PCI_CONFIG_HEADER)) {
case PCI_HEADER_SINGLE_FUNCTION:
case PCI_HEADER_MULTI_FUNCTION:
switch (address) {
case PCI_CONFIG_VENDOR_ID:
case PCI_CONFIG_VENDOR_ID + 1:
case PCI_CONFIG_DEVICE_ID:
case PCI_CONFIG_DEVICE_ID + 1:
case PCI_CONFIG_REVISION:
case PCI_CONFIG_REVISION + 1:
case PCI_CONFIG_CLASS_DEVICE:
case PCI_CONFIG_CLASS_DEVICE + 1:
case PCI_CONFIG_HEADER:
case PCI_CONFIG_BASE_ADDRESS:
case PCI_CONFIG_BASE_ADDRESS + 0x01:
case PCI_CONFIG_BASE_ADDRESS + 0x02:
case PCI_CONFIG_BASE_ADDRESS + 0x03:
case PCI_CONFIG_BASE_ADDRESS + 0x04:
case PCI_CONFIG_BASE_ADDRESS + 0x05:
case PCI_CONFIG_BASE_ADDRESS + 0x06:
case PCI_CONFIG_BASE_ADDRESS + 0x07:
case PCI_CONFIG_BASE_ADDRESS + 0x08:
case PCI_CONFIG_BASE_ADDRESS + 0x09:
case PCI_CONFIG_BASE_ADDRESS + 0x0a:
case PCI_CONFIG_BASE_ADDRESS + 0x0b:
case PCI_CONFIG_BASE_ADDRESS + 0x0c:
case PCI_CONFIG_BASE_ADDRESS + 0x0d:
case PCI_CONFIG_BASE_ADDRESS + 0x0e:
case PCI_CONFIG_BASE_ADDRESS + 0x0f:
case PCI_CONFIG_BASE_ADDRESS + 0x10:
case PCI_CONFIG_BASE_ADDRESS + 0x11:
case PCI_CONFIG_BASE_ADDRESS + 0x12:
case PCI_CONFIG_BASE_ADDRESS + 0x13:
case PCI_CONFIG_BASE_ADDRESS + 0x14:
case PCI_CONFIG_BASE_ADDRESS + 0x15:
case PCI_CONFIG_BASE_ADDRESS + 0x16:
case PCI_CONFIG_BASE_ADDRESS + 0x17:
case PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS:
case PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS + 0x1:
case PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS + 0x2:
case PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS + 0x3:
case PCI_CONFIG_INTERRUPT_PIN:
return false;
default:
return true;
}
default:
case PCI_HEADER_PCI_PCI_BRIDGE:
switch (address) {
case PCI_CONFIG_VENDOR_ID:
case PCI_CONFIG_VENDOR_ID + 1:
case PCI_CONFIG_DEVICE_ID:
case PCI_CONFIG_DEVICE_ID + 1:
case PCI_CONFIG_REVISION:
case PCI_CONFIG_REVISION + 1:
case PCI_CONFIG_CLASS_DEVICE:
case PCI_CONFIG_CLASS_DEVICE + 1:
case PCI_CONFIG_HEADER:
case 0x38: //RESERVED
case 0x39: //RESERVED
case 0x3a: //RESERVED
case 0x3b: //RESERVED
case PCI_CONFIG_INTERRUPT_PIN:
return false;
default:
return true;
}
}
}
public boolean configWriteByte(int address, byte data) //returns true if device needs remapping
{
if (checkConfigWrite(address))
putConfigByte(address, data);
if (address >= PCI_CONFIG_COMMAND && address < (PCI_CONFIG_COMMAND + 2))
/* if the command register is modified, we must modify the mappings */
return true;
return false;
}
public final boolean configWriteWord(int address, short data) //returns true if device needs remapping
{
int modAddress = address;
for (int i = 0; i < 2; i++) {
if (checkConfigWrite(modAddress))
putConfigByte(modAddress, (byte) data);
modAddress++;
data >>>= 8;
}
if ((modAddress > PCI_CONFIG_COMMAND) && (address < (PCI_CONFIG_COMMAND + 2)))
// if the command register is modified, we must modify the mappings
return true;
return false;
}
public final boolean configWriteLong(int address, int data)
{
if (((address >= PCI_CONFIG_BASE_ADDRESS && address < (PCI_CONFIG_BASE_ADDRESS + 4 * 6)) || (address >= PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS && address < (PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS + 4)))) {
int regionIndex;
if (address >= PCI_CONFIG_EXPANSION_ROM_BASE_ADDRESS)
regionIndex = PCI_ROM_SLOT;
else
regionIndex = (address - PCI_CONFIG_BASE_ADDRESS) >>> 2;
IORegion r = getIORegion(regionIndex);
if (r != null) {
if (regionIndex == PCI_ROM_SLOT)
data &= (~(r.getSize() - 1)) | 1;
else {
data &= ~(r.getSize() - 1);
data |= r.getType();
}
putConfigLong(address, data);
return true;
}
}
int modAddress = address;
for (int i = 0; i < 4; i++) {
if (checkConfigWrite(modAddress))
putConfigByte(modAddress, (byte) data);
modAddress++;
data = data >>> 8;
}
if (modAddress > PCI_CONFIG_COMMAND && address < (PCI_CONFIG_COMMAND + 2))
/* if the command register is modified, we must modify the mappings */
return true;
return false;
}
public final byte configReadByte(int address)
{
return configuration[address];
}
public final short configReadWord(int address)
{
short result = configReadByte(address + 1);
result <<= 8;
result |= (0xff & configReadByte(address));
return result;
}
public final int configReadLong(int address)
{
int result = 0xffff & configReadWord(address + 2);
result <<= 16;
result |= (0xffff & configReadWord(address));
return result;
}
public final void putConfigByte(int address, byte data)
{
configuration[address] = data;
}
public final void putConfigWord(int address, short data)
{
putConfigByte(address, (byte) data);
address++;
data >>= 8;
putConfigByte(address, (byte) data);
}
public final void putConfigLong(int address, int data)
{
putConfigWord(address, (short) data);
address += 2;
data >>= 16;
putConfigWord(address, (short) data);
}
public void setIRQIndex(int irqIndex)
{
irq = irqIndex;
}
public int getIRQIndex()
{
return irq;
}
public void addIRQBouncer(IRQBouncer bouncer)
{
irqBouncer = bouncer;
}
public IRQBouncer getIRQBouncer()
{
return irqBouncer;
}
public boolean initialised()
{
return pciRegistered;
}
public void reset()
{
pciRegistered = false;
}
public void acceptComponent(HardwareComponent component)
{
if ((component instanceof PCIBus) && component.initialised() && !pciRegistered)
pciRegistered = ((PCIBus) component).registerDevice(this);
}
public void updateComponent(org.jpc.emulator.HardwareComponent component)
{
if ((component instanceof PCIBus) && component.updated() && !pciRegistered)
pciRegistered = ((PCIBus) component).registerDevice(this);
}
public boolean updated()
{
return initialised();
}
}