mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-09-29 17:56:59 +00:00
Merge pull request #41 from joevt/fix-pci-unaligned
Fix PCI config r/w of byte and word and unaligned.
This commit is contained in:
commit
699b62373a
@ -41,9 +41,11 @@ const int MultiplyDeBruijnBitPosition2[] =
|
||||
#define WHAT_BIT_SET(val) (MultiplyDeBruijnBitPosition2[(uint32_t)(val * 0x077CB531U) >> 27])
|
||||
|
||||
Bandit::Bandit(int bridge_num, std::string name, int dev_id, int rev)
|
||||
: PCIHost(), PCIDevice(name)
|
||||
: BanditHost()
|
||||
{
|
||||
supports_types(HWCompType::PCI_HOST | HWCompType::PCI_DEV);
|
||||
this->name = name;
|
||||
|
||||
supports_types(HWCompType::PCI_HOST);
|
||||
|
||||
this->base_addr = 0xF0000000 + ((bridge_num & 3) << 25);
|
||||
|
||||
@ -58,6 +60,15 @@ Bandit::Bandit(int bridge_num, std::string name, int dev_id, int rev)
|
||||
// base_addr + 0x1000000 --> pass-through memory space (not included below)
|
||||
mem_ctrl->add_mmio_region(base_addr, 0x01000000, this);
|
||||
|
||||
std::string banditpcitname = "Bandit1PCI";
|
||||
attach_pci_device(banditpcitname, 1);
|
||||
}
|
||||
|
||||
BanditPCI::BanditPCI(int bridge_num, std::string name)
|
||||
: PCIDevice(name)
|
||||
{
|
||||
supports_types(HWCompType::PCI_DEV);
|
||||
|
||||
// prepare the PCI config header
|
||||
this->vendor_id = PCI_VENDOR_APPLE;
|
||||
this->device_id = 0x0001;
|
||||
@ -80,195 +91,149 @@ Bandit::Bandit(int bridge_num, std::string name, int dev_id, int rev)
|
||||
this->rd_hold_off_cnt = 8;
|
||||
}
|
||||
|
||||
uint32_t Bandit::pci_cfg_read(uint32_t reg_offs, uint32_t size)
|
||||
uint32_t BanditPCI::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
return PCIDevice::pci_cfg_read(reg_offs, size);
|
||||
return PCIDevice::pci_cfg_read(reg_offs, details);
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case BANDIT_ADDR_MASK:
|
||||
return BYTESWAP_32(this->addr_mask);
|
||||
return this->addr_mask;
|
||||
case BANDIT_MODE_SELECT:
|
||||
return BYTESWAP_32(this->mode_ctrl);
|
||||
return this->mode_ctrl;
|
||||
case BANDIT_ARBUS_RD_HOLD_OFF:
|
||||
return BYTESWAP_32(this->rd_hold_off_cnt);
|
||||
default:
|
||||
LOG_F(WARNING, "%s: reading from unimplemented config register at 0x%X",
|
||||
this->pci_name.c_str(), reg_offs);
|
||||
return this->rd_hold_off_cnt;
|
||||
}
|
||||
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Bandit::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
void BanditPCI::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, size);
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 4) {
|
||||
value = BYTESWAP_32(value);
|
||||
} else {
|
||||
LOG_F(WARNING, "%s: non-DWORD writes to the control registers not supported",
|
||||
this->pci_name.c_str());
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case BANDIT_ADDR_MASK:
|
||||
this->addr_mask = value;
|
||||
this->verbose_address_space();
|
||||
break;
|
||||
return;
|
||||
case BANDIT_MODE_SELECT:
|
||||
this->mode_ctrl = value;
|
||||
break;
|
||||
return;
|
||||
case BANDIT_ARBUS_RD_HOLD_OFF:
|
||||
this->rd_hold_off_cnt = value & 0x1F;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: writing to unimplemented config register at 0x%X",
|
||||
this->pci_name.c_str(), reg_offs);
|
||||
this->rd_hold_off_cnt = value & 0x1F;
|
||||
return;
|
||||
}
|
||||
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
|
||||
void BanditHost::cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device)
|
||||
{
|
||||
device = NULL;
|
||||
details.size = size;
|
||||
details.offset = offset & 3;
|
||||
fun_num = (this->config_addr >> 8) & 7;
|
||||
reg_offs = this->config_addr & 0xFCU;
|
||||
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
|
||||
details.flags = PCI_CONFIG_TYPE_1;
|
||||
bus_num = (this->config_addr >> 16) & 255;
|
||||
dev_num = (this->config_addr >> 11) & 31;
|
||||
device = pci_find_device(bus_num, dev_num, fun_num);
|
||||
return;
|
||||
}
|
||||
details.flags = PCI_CONFIG_TYPE_0;
|
||||
bus_num = 0; // bus number is meaningless for type 0 configuration command; a type 1 configuration command cannot reach devices attached directly to the host
|
||||
uint32_t idsel = this->config_addr & 0xFFFFF800U;
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
for (dev_num = -1, idsel = this->config_addr; idsel; idsel >>= 1, dev_num++) {}
|
||||
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL", this->name.c_str(), (uint32_t)this->config_addr);
|
||||
return;
|
||||
}
|
||||
dev_num = WHAT_BIT_SET(idsel);
|
||||
if (this->dev_map.count(idsel >> 11)) {
|
||||
device = this->dev_map[idsel >> 11];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Bandit::read(uint32_t rgn_start, uint32_t offset, int size)
|
||||
uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
|
||||
{
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
uint32_t result, idsel;
|
||||
switch (offset >> 22) {
|
||||
|
||||
if (offset & BANDIT_CONFIG_SPACE) {
|
||||
if (offset & 0x00400000) {
|
||||
fun_num = (this->config_addr >> 8) & 7;
|
||||
reg_offs = this->config_addr & 0xFCU;
|
||||
|
||||
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
|
||||
if (this->config_addr & BANDIT_CAR_TYPE) {
|
||||
bus_num = (this->config_addr >> 16) & 255;
|
||||
dev_num = (this->config_addr >> 11) & 31;
|
||||
LOG_F(
|
||||
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
case 3: // 0xC00000 // CONFIG_DATA
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
AccessDetails details;
|
||||
PCIDevice *device;
|
||||
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||
details.flags |= PCI_CONFIG_READ;
|
||||
if (device) {
|
||||
return pci_cfg_rev_read(device->pci_cfg_read(reg_offs, details), details);
|
||||
}
|
||||
LOG_READ_NON_EXISTENT_PCI_DEVICE();
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
|
||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
||||
case 2: // 0x800000 // CONFIG_ADDR
|
||||
return this->config_addr;
|
||||
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
LOG_F(
|
||||
ERROR, "%s: read invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c",
|
||||
this->name.c_str(), idsel, this->config_addr,
|
||||
fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
|
||||
if (idsel == BANDIT_ID_SEL) { // access to myself
|
||||
result = this->pci_cfg_read(reg_offs, size);
|
||||
} else {
|
||||
if (this->dev_map.count(idsel)) {
|
||||
result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size);
|
||||
} else {
|
||||
dev_num = WHAT_BIT_SET(idsel) + 11;
|
||||
LOG_F(
|
||||
ERROR, "%s err: read attempt from non-existing PCI device ??:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
default: // 0x000000 // I/O space
|
||||
{
|
||||
uint32_t result;
|
||||
// broadcast I/O request to devices that support I/O space
|
||||
// until a device returns true that means "request accepted"
|
||||
for (auto& dev : this->io_space_devs) {
|
||||
if (dev->pci_io_read(offset, size, &result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = this->config_addr;
|
||||
LOG_F(ERROR, "%s: attempt to read from unmapped PCI I/O space, offset=0x%X",
|
||||
this->name.c_str(), offset);
|
||||
return 0;
|
||||
}
|
||||
} else { // I/O space access
|
||||
// broadcast I/O request to devices that support I/O space
|
||||
// until a device returns true that means "request accepted"
|
||||
for (auto& dev : this->io_space_devs) {
|
||||
if (dev->pci_io_read(offset, size, &result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
LOG_F(ERROR, "%s: attempt to read from unmapped PCI I/O space, offset=0x%X",
|
||||
this->name.c_str(), offset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Bandit::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
||||
void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
||||
{
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
uint32_t idsel;
|
||||
switch (offset >> 22) {
|
||||
|
||||
if (offset & BANDIT_CONFIG_SPACE) {
|
||||
if (offset & 0x00400000) {
|
||||
fun_num = (this->config_addr >> 8) & 7;
|
||||
reg_offs = this->config_addr & 0xFCU;
|
||||
|
||||
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
|
||||
if (this->config_addr & BANDIT_CAR_TYPE) {
|
||||
bus_num = (this->config_addr >> 16) & 255;
|
||||
dev_num = (this->config_addr >> 11) & 31;
|
||||
LOG_F(
|
||||
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
case 3: // 0xC00000 // CONFIG_DATA
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
AccessDetails details;
|
||||
PCIDevice *device;
|
||||
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||
details.flags |= PCI_CONFIG_WRITE;
|
||||
if (device) {
|
||||
uint32_t oldvalue = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
|
||||
value = pci_cfg_rev_write(oldvalue, details, value);
|
||||
device->pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
|
||||
break;
|
||||
|
||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
||||
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
LOG_F(
|
||||
ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x",
|
||||
this->name.c_str(), idsel, this->config_addr,
|
||||
fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (idsel == BANDIT_ID_SEL) { // access to myself
|
||||
this->pci_cfg_write(reg_offs, value, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->dev_map.count(idsel)) {
|
||||
this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size);
|
||||
} else {
|
||||
dev_num = WHAT_BIT_SET(idsel) + 11;
|
||||
LOG_F(
|
||||
ERROR, "%s err: write attempt to non-existing PCI device ??:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
case 2: // 0x800000 // CONFIG_ADDR
|
||||
this->config_addr = BYTESWAP_32(value);
|
||||
}
|
||||
} else { // I/O space access
|
||||
// broadcast I/O request to devices that support I/O space
|
||||
// until a device returns true that means "request accepted"
|
||||
for (auto& dev : this->io_space_devs) {
|
||||
if (dev->pci_io_write(offset, value, size)) {
|
||||
return;
|
||||
break;
|
||||
|
||||
default: // 0x000000 // I/O space
|
||||
// broadcast I/O request to devices that support I/O space
|
||||
// until a device returns true that means "request accepted"
|
||||
for (auto& dev : this->io_space_devs) {
|
||||
if (dev->pci_io_write(offset, value, size)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_F(ERROR, "%s: attempt to write to unmapped PCI I/O space, offset=0x%X",
|
||||
this->name.c_str(), offset);
|
||||
LOG_F(ERROR, "%s: attempt to write to unmapped PCI I/O space, offset=0x%X",
|
||||
this->name.c_str(), offset);
|
||||
}
|
||||
}
|
||||
|
||||
void Bandit::verbose_address_space()
|
||||
void BanditPCI::verbose_address_space()
|
||||
{
|
||||
uint32_t mask;
|
||||
int bit_pos;
|
||||
@ -296,7 +261,7 @@ void Bandit::verbose_address_space()
|
||||
}
|
||||
}
|
||||
|
||||
Chaos::Chaos(std::string name) : PCIHost()
|
||||
Chaos::Chaos(std::string name) : BanditHost()
|
||||
{
|
||||
this->name = name;
|
||||
|
||||
@ -312,118 +277,6 @@ Chaos::Chaos(std::string name) : PCIHost()
|
||||
mem_ctrl->add_mmio_region(0xF0000000UL, 0x01000000, this);
|
||||
}
|
||||
|
||||
uint32_t Chaos::read(uint32_t rgn_start, uint32_t offset, int size)
|
||||
{
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
uint32_t result, idsel;
|
||||
|
||||
if (offset & BANDIT_CONFIG_SPACE) {
|
||||
if (offset & 0x00400000) {
|
||||
fun_num = (this->config_addr >> 8) & 7;
|
||||
reg_offs = this->config_addr & 0xFCU;
|
||||
|
||||
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
|
||||
if (this->config_addr & BANDIT_CAR_TYPE) {
|
||||
bus_num = (this->config_addr >> 16) & 255;
|
||||
dev_num = (this->config_addr >> 11) & 31;
|
||||
LOG_F(
|
||||
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
|
||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
||||
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
LOG_F(
|
||||
ERROR, "%s: read invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c",
|
||||
this->name.c_str(), idsel, this->config_addr,
|
||||
fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
|
||||
if (this->dev_map.count(idsel)) {
|
||||
result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size);
|
||||
} else {
|
||||
dev_num = WHAT_BIT_SET(idsel) + 11;
|
||||
LOG_F(
|
||||
ERROR, "%s err: read attempt from non-existing VCI device ??:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
} else {
|
||||
result = this->config_addr;
|
||||
}
|
||||
} else { // I/O space access
|
||||
LOG_F(ERROR, "%s: I/O space not supported", this->name.c_str());
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Chaos::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
||||
{
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
uint32_t idsel;
|
||||
|
||||
if (offset & BANDIT_CONFIG_SPACE) {
|
||||
if (offset & 0x00400000) {
|
||||
fun_num = (this->config_addr >> 8) & 7;
|
||||
reg_offs = this->config_addr & 0xFCU;
|
||||
|
||||
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
|
||||
if (this->config_addr & BANDIT_CAR_TYPE) {
|
||||
bus_num = (this->config_addr >> 16) & 255;
|
||||
dev_num = (this->config_addr >> 11) & 31;
|
||||
LOG_F(
|
||||
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
||||
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
LOG_F(
|
||||
ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x",
|
||||
this->name.c_str(), idsel, this->config_addr,
|
||||
fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->dev_map.count(idsel)) {
|
||||
this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size);
|
||||
} else {
|
||||
dev_num = WHAT_BIT_SET(idsel) + 11;
|
||||
LOG_F(
|
||||
ERROR, "%s err: write attempt to non-existing VCI device ??:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this->config_addr = BYTESWAP_32(value);
|
||||
}
|
||||
} else { // I/O space access
|
||||
LOG_F(ERROR, "%s: I/O space not supported", this->name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static const DeviceDescription Bandit1_Descriptor = {
|
||||
Bandit::create_first, {}, {}
|
||||
};
|
||||
@ -436,6 +289,11 @@ static const DeviceDescription Chaos_Descriptor = {
|
||||
Chaos::create, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Bandit1, Bandit1_Descriptor);
|
||||
REGISTER_DEVICE(PsxPci1, PsxPci1_Descriptor);
|
||||
REGISTER_DEVICE(Chaos, Chaos_Descriptor);
|
||||
static const DeviceDescription Bandit1PCI_Descriptor = {
|
||||
BanditPCI::create_first, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Bandit1 , Bandit1_Descriptor);
|
||||
REGISTER_DEVICE(Bandit1PCI, Bandit1PCI_Descriptor);
|
||||
REGISTER_DEVICE(PsxPci1 , PsxPci1_Descriptor);
|
||||
REGISTER_DEVICE(Chaos , Chaos_Descriptor);
|
||||
|
@ -38,7 +38,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#define BANDIT_ID_SEL (1 << 0) // Bandit's own IDSEL
|
||||
#define BANDIT_DEV (11) // Bandit's own device number
|
||||
#define BANDIT_CAR_TYPE (1 << 0) // Bandit config address type bit
|
||||
#define BANDIT_CONFIG_SPACE 0x00800000 // Bandit Config Space bit
|
||||
|
||||
@ -52,7 +52,19 @@ enum {
|
||||
/** checks if one bit is set at time, return 0 if not */
|
||||
#define SINGLE_BIT_SET(val) ((val) && !((val) & ((val)-1)))
|
||||
|
||||
class Bandit : public PCIHost, public PCIDevice {
|
||||
class BanditHost : public PCIHost, public MMIODevice {
|
||||
public:
|
||||
void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device);
|
||||
|
||||
// MMIODevice methods
|
||||
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
|
||||
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
|
||||
|
||||
protected:
|
||||
uint32_t config_addr;
|
||||
};
|
||||
|
||||
class Bandit : public BanditHost {
|
||||
public:
|
||||
Bandit(int bridge_num, std::string name, int dev_id=1, int rev=3);
|
||||
~Bandit() = default;
|
||||
@ -65,19 +77,27 @@ public:
|
||||
return std::unique_ptr<Bandit>(new Bandit(1, "PSX-PCI1", 8, 0));
|
||||
};
|
||||
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
|
||||
private:
|
||||
uint32_t base_addr;
|
||||
};
|
||||
|
||||
// MMIODevice methods
|
||||
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
|
||||
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
|
||||
class BanditPCI : public PCIDevice {
|
||||
public:
|
||||
BanditPCI(int bridge_num, std::string name);
|
||||
~BanditPCI() = default;
|
||||
|
||||
static std::unique_ptr<HWComponent> create_first() {
|
||||
return std::unique_ptr<BanditPCI>(new BanditPCI(1, "BanditPCI"));
|
||||
};
|
||||
|
||||
// PCIDevice methods
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||
|
||||
protected:
|
||||
void verbose_address_space();
|
||||
|
||||
private:
|
||||
uint32_t base_addr;
|
||||
uint32_t config_addr;
|
||||
uint32_t addr_mask;
|
||||
uint32_t mode_ctrl; // controls various chip modes/features
|
||||
uint32_t rd_hold_off_cnt;
|
||||
@ -88,7 +108,7 @@ private:
|
||||
frequency as the CPU bus (40-50 MHz) and provides an interface
|
||||
between video input/output devices and the CPU bus.
|
||||
*/
|
||||
class Chaos : public PCIHost, public MMIODevice {
|
||||
class Chaos : public BanditHost {
|
||||
public:
|
||||
Chaos(std::string name);
|
||||
~Chaos() = default;
|
||||
@ -96,13 +116,6 @@ public:
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<Chaos>(new Chaos("VCI0"));
|
||||
};
|
||||
|
||||
// MMIODevice methods
|
||||
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
|
||||
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
|
||||
|
||||
private:
|
||||
uint32_t config_addr;
|
||||
};
|
||||
|
||||
#endif // BANDIT_PCI_H
|
||||
|
@ -50,81 +50,49 @@ PCIDevice::PCIDevice(std::string name)
|
||||
this->pci_notify_bar_change = [](int bar_num) {};
|
||||
};
|
||||
|
||||
uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, uint32_t size)
|
||||
uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
switch (reg_offs) {
|
||||
case PCI_CFG_DEV_ID:
|
||||
result = (this->device_id << 16) | (this->vendor_id);
|
||||
break;
|
||||
return (this->device_id << 16) | (this->vendor_id);
|
||||
case PCI_CFG_STAT_CMD:
|
||||
result = (this->pci_rd_stat() << 16) | (this->pci_rd_cmd());
|
||||
break;
|
||||
return (this->pci_rd_stat() << 16) | (this->pci_rd_cmd());
|
||||
case PCI_CFG_CLASS_REV:
|
||||
result = this->class_rev;
|
||||
break;
|
||||
return this->class_rev;
|
||||
case PCI_CFG_DWORD_3:
|
||||
result = (pci_rd_bist() << 24) | (this->hdr_type << 16) |
|
||||
(pci_rd_lat_timer() << 8) | pci_rd_cache_lnsz();
|
||||
break;
|
||||
return (pci_rd_bist() << 24) | (this->hdr_type << 16) |
|
||||
(pci_rd_lat_timer() << 8) | pci_rd_cache_lnsz();
|
||||
case PCI_CFG_BAR0:
|
||||
case PCI_CFG_BAR1:
|
||||
case PCI_CFG_BAR2:
|
||||
case PCI_CFG_BAR3:
|
||||
case PCI_CFG_BAR4:
|
||||
case PCI_CFG_BAR5:
|
||||
result = this->bars[(reg_offs - 0x10) >> 2];
|
||||
break;
|
||||
return this->bars[(reg_offs - 0x10) >> 2];
|
||||
case PCI_CFG_SUBSYS_ID:
|
||||
result = (this->subsys_id << 16) | (this->subsys_vndr);
|
||||
break;
|
||||
return (this->subsys_id << 16) | (this->subsys_vndr);
|
||||
case PCI_CFG_ROM_BAR:
|
||||
result = this->exp_rom_bar;
|
||||
break;
|
||||
return this->exp_rom_bar;
|
||||
case PCI_CFG_DWORD_15:
|
||||
result = (max_lat << 24) | (min_gnt << 16) | (irq_pin << 8) | irq_line;
|
||||
break;
|
||||
return (max_lat << 24) | (min_gnt << 16) | (irq_pin << 8) | irq_line;
|
||||
case PCI_CFG_CAP_PTR:
|
||||
result = cap_ptr;
|
||||
break;
|
||||
default:
|
||||
LOG_F(
|
||||
WARNING, "%s: attempt to read from reserved/unimplemented register @%02x.%c",
|
||||
this->pci_name.c_str(), reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 4) {
|
||||
return BYTESWAP_32(result);
|
||||
} else {
|
||||
return read_mem_rev(((uint8_t *)&result) + (reg_offs & 3), size);
|
||||
return cap_ptr;
|
||||
}
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
|
||||
{
|
||||
uint32_t data;
|
||||
|
||||
if (size == 4) {
|
||||
data = BYTESWAP_32(value);
|
||||
} else {
|
||||
// get current register content as DWORD and update it partially
|
||||
data = BYTESWAP_32(this->pci_cfg_read(reg_offs, 4));
|
||||
write_mem_rev(((uint8_t *)&data) + (reg_offs & 3), value, size);
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case PCI_CFG_STAT_CMD:
|
||||
this->pci_wr_stat(data >> 16);
|
||||
this->pci_wr_cmd(data & 0xFFFFU);
|
||||
this->pci_wr_stat(value >> 16);
|
||||
this->pci_wr_cmd(value & 0xFFFFU);
|
||||
break;
|
||||
case PCI_CFG_DWORD_3:
|
||||
this->pci_wr_bist(data >> 24);
|
||||
this->pci_wr_lat_timer((data >> 8) & 0xFF);
|
||||
this->pci_wr_cache_lnsz(data & 0xFF);
|
||||
this->pci_wr_bist(value >> 24);
|
||||
this->pci_wr_lat_timer((value >> 8) & 0xFF);
|
||||
this->pci_wr_cache_lnsz(value & 0xFF);
|
||||
break;
|
||||
case PCI_CFG_BAR0:
|
||||
case PCI_CFG_BAR1:
|
||||
@ -132,17 +100,17 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
case PCI_CFG_BAR3:
|
||||
case PCI_CFG_BAR4:
|
||||
case PCI_CFG_BAR5:
|
||||
if (data == 0xFFFFFFFFUL) {
|
||||
if (value == 0xFFFFFFFFUL) {
|
||||
this->do_bar_sizing((reg_offs - 0x10) >> 2);
|
||||
} else {
|
||||
this->set_bar_value((reg_offs - 0x10) >> 2, data);
|
||||
this->set_bar_value((reg_offs - 0x10) >> 2, value);
|
||||
}
|
||||
break;
|
||||
case PCI_CFG_ROM_BAR:
|
||||
if ((data & this->exp_bar_cfg) == this->exp_bar_cfg) {
|
||||
this->exp_rom_bar = (data & (this->exp_bar_cfg | 1));
|
||||
if ((value & this->exp_bar_cfg) == this->exp_bar_cfg) {
|
||||
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
|
||||
} else {
|
||||
this->exp_rom_bar = (data & (this->exp_bar_cfg | 1));
|
||||
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
|
||||
if (this->exp_rom_bar & 1) {
|
||||
this->map_exp_rom_mem();
|
||||
} else {
|
||||
@ -152,15 +120,10 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
}
|
||||
break;
|
||||
case PCI_CFG_DWORD_15:
|
||||
this->irq_line = data >> 24;
|
||||
this->irq_line = value >> 24;
|
||||
break;
|
||||
default:
|
||||
LOG_F(
|
||||
WARNING, "%s: attempt to write to reserved/unimplemented register @%02x.%c = %0*x",
|
||||
this->pci_name.c_str(), reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,8 @@ public:
|
||||
};
|
||||
|
||||
// configuration space access methods
|
||||
virtual uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size);
|
||||
virtual void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
|
||||
virtual uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||
virtual void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||
|
||||
// plugin interface for using in the derived classes
|
||||
std::function<uint16_t()> pci_rd_stat;
|
||||
@ -101,6 +101,10 @@ public:
|
||||
this->host_instance = host_instance;
|
||||
};
|
||||
|
||||
// MMIODevice methods
|
||||
virtual uint32_t read(uint32_t rgn_start, uint32_t offset, int size) { return 0; }
|
||||
virtual void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) { }
|
||||
|
||||
protected:
|
||||
void do_bar_sizing(int bar_num);
|
||||
void set_bar_value(int bar_num, uint32_t value);
|
||||
@ -137,4 +141,124 @@ protected:
|
||||
std::unique_ptr<uint8_t[]> exp_rom_data;
|
||||
};
|
||||
|
||||
/* value is dword from PCI config. MSB..LSB of value is stored in PCI config as 0:LSB..3:MSB.
|
||||
result is part of value at byte offset from LSB with size bytes (with wrap around) and flipped as required for pci_cfg_read result. */
|
||||
inline uint32_t pci_cfg_rev_read(uint32_t value, AccessDetails &details) {
|
||||
switch (details.size << 2 | details.offset) {
|
||||
case 0x04: return value & 0xff; // 0
|
||||
case 0x05: return (value >> 8) & 0xff; // 1
|
||||
case 0x06: return (value >> 16) & 0xff; // 2
|
||||
case 0x07: return (value >> 24) & 0xff; // 3
|
||||
|
||||
case 0x08: return ((value & 0xff) << 8) | ((value >> 8) & 0xff); // 0 1
|
||||
case 0x09: return ( value & 0xff00) | ((value >> 16) & 0xff); // 1 2
|
||||
case 0x0a: return ((value >> 8) & 0xff00) | ((value >> 24) & 0xff); // 2 3
|
||||
case 0x0b: return ((value >> 16) & 0xff00) | ( value & 0xff); // 3 0
|
||||
|
||||
case 0x10: return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value >> 8) & 0xff00) | ((value >> 24) & 0xff); // 0 1 2 3
|
||||
case 0x11: return ((value & 0xff00) << 16) | ( value & 0xff0000) | ((value >> 16) & 0xff00) | ( value & 0xff); // 1 2 3 0
|
||||
case 0x12: return ((value & 0xff0000) << 8) | ((value >> 8) & 0xff0000) | ((value & 0xff) << 8) | ((value >> 8) & 0xff); // 2 3 0 1
|
||||
case 0x13: return ( value & 0xff000000) | ((value & 0xff) << 16) | ( value & 0xff00) | ((value >> 16) & 0xff); // 3 0 1 2
|
||||
|
||||
default: return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/* value is dword from PCI config. MSB..LSB of value (3.2.1.0) is stored in PCI config as 0:LSB..3:MSB.
|
||||
newvalue is flipped bytes (d0.d1.d2.d3, as passed to pci_cfg_write) to be merged into value.
|
||||
result is part of value at byte offset from LSB with size bytes (with wrap around) modified by newvalue. */
|
||||
inline uint32_t pci_cfg_rev_write(uint32_t value, AccessDetails &details, uint32_t newvalue) {
|
||||
switch (details.size << 2 | details.offset) {
|
||||
case 0x04: return (value & 0xffffff00) | (newvalue & 0xff); // 3 2 1 d0
|
||||
case 0x05: return (value & 0xffff00ff) | ((newvalue & 0xff) << 8); // 3 2 d0 0
|
||||
case 0x06: return (value & 0xff00ffff) | ((newvalue & 0xff) << 16); // 3 d0 1 0
|
||||
case 0x07: return (value & 0x00ffffff) | ((newvalue & 0xff) << 24); // d0 2 1 0
|
||||
|
||||
case 0x08: return (value & 0xffff0000) | ((newvalue >> 8) & 0xff) | ((newvalue & 0xff) << 8); // 3 2 d1 d0
|
||||
case 0x09: return (value & 0xff0000ff) | (newvalue & 0xff00) | ((newvalue & 0xff) << 16); // 3 d1 d0 0
|
||||
case 0x0a: return (value & 0x0000ffff) | ((newvalue & 0xff00) << 8) | ((newvalue & 0xff) << 24); // d1 d0 1 0
|
||||
case 0x0b: return (value & 0x00ffff00) | ((newvalue & 0xff00) << 16) | (newvalue & 0xff); // d0 2 1 d1
|
||||
|
||||
case 0x10: return ((newvalue & 0xff) << 24) | ((newvalue & 0xff00) << 8) | ((newvalue >> 8) & 0xff00) | ((newvalue >> 24) & 0xff); // d3 d2 d1 d0
|
||||
case 0x11: return ((newvalue & 0xff00) << 16) | ( newvalue & 0xff0000) | ((newvalue >> 16) & 0xff00) | ( newvalue & 0xff); // d2 d1 d0 d3
|
||||
case 0x12: return ((newvalue & 0xff0000) << 8) | ((newvalue >> 8) & 0xff0000) | ((newvalue & 0xff) << 8) | ((newvalue >> 8) & 0xff); // d1 d0 d3 d2
|
||||
case 0x13: return ( newvalue & 0xff000000) | ((newvalue & 0xff) << 16) | ( newvalue & 0xff00) | ((newvalue >> 16) & 0xff); // d0 d3 d2 d1
|
||||
|
||||
default: return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t pci_cfg_log(uint32_t value, AccessDetails &details) {
|
||||
switch (details.size << 2 | details.offset) {
|
||||
case 0x04: return (uint8_t) value;
|
||||
case 0x05: return (uint8_t)(value >> 8);
|
||||
case 0x06: return (uint8_t)(value >> 16);
|
||||
case 0x07: return (uint8_t)(value >> 24);
|
||||
|
||||
case 0x08: return (uint16_t) value;
|
||||
case 0x09: return (uint16_t) (value >> 8);
|
||||
case 0x0a: return (uint16_t) (value >> 16);
|
||||
case 0x0b: return (uint16_t)((value >> 24) | (value << 8));
|
||||
|
||||
case 0x10: return value;
|
||||
case 0x11: return (value >> 8) | (value << 24);
|
||||
case 0x12: return (value >> 16) | (value << 16);
|
||||
case 0x13: return (value >> 24) | (value << 8);
|
||||
|
||||
default: return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
#define SIZE_ARGS details.size == 4 ? 'l' : details.size == 2 ? 'w' : details.size == 1 ? 'b' : '0' + details.size
|
||||
|
||||
#define LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER() \
|
||||
do { if ((details.flags & PCI_CONFIG_DIRECTION) == PCI_CONFIG_READ) { \
|
||||
VLOG_F( \
|
||||
(~-details.size & details.offset) ? loguru::Verbosity_ERROR : loguru::Verbosity_WARNING, \
|
||||
"%s: read unimplemented config register @%02x.%c", \
|
||||
this->name.c_str(), reg_offs + details.offset, \
|
||||
SIZE_ARGS \
|
||||
); \
|
||||
} } while(0)
|
||||
|
||||
#define LOG_NAMED_CONFIG_REGISTER(reg_verb, reg_name) \
|
||||
VLOG_F( \
|
||||
(~-details.size & details.offset) ? loguru::Verbosity_ERROR : loguru::Verbosity_WARNING, \
|
||||
"%s: %s %s register @%02x.%c = %0*x", \
|
||||
this->name.c_str(), reg_verb, reg_name, reg_offs + details.offset, \
|
||||
SIZE_ARGS, \
|
||||
details.size * 2, pci_cfg_log(value, details) \
|
||||
)
|
||||
|
||||
#define LOG_READ_NAMED_CONFIG_REGISTER(reg_name) \
|
||||
do { if ((details.flags & PCI_CONFIG_DIRECTION) == PCI_CONFIG_READ) { \
|
||||
LOG_NAMED_CONFIG_REGISTER("read ", reg_name); \
|
||||
} } while(0)
|
||||
|
||||
#define LOG_WRITE_NAMED_CONFIG_REGISTER(reg_name) \
|
||||
LOG_NAMED_CONFIG_REGISTER("write", reg_name)
|
||||
|
||||
#define LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER_WITH_VALUE() \
|
||||
LOG_READ_NAMED_CONFIG_REGISTER("unimplemented config")
|
||||
|
||||
#define LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER() \
|
||||
LOG_WRITE_NAMED_CONFIG_REGISTER("unimplemented config")
|
||||
|
||||
#define LOG_READ_NON_EXISTENT_PCI_DEVICE() \
|
||||
LOG_F( \
|
||||
ERROR, \
|
||||
"%s err: read attempt from non-existent PCI device %02x:%02x.%x @%02x.%c", \
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + details.offset, \
|
||||
SIZE_ARGS \
|
||||
)
|
||||
|
||||
#define LOG_WRITE_NON_EXISTENT_PCI_DEVICE() \
|
||||
LOG_F( \
|
||||
ERROR, \
|
||||
"%s err: write attempt to non-existent PCI device %02x:%02x.%x @%02x.%c = %0*x", \
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + details.offset, \
|
||||
SIZE_ARGS, \
|
||||
details.size * 2, BYTESWAP_SIZED(value, details.size) \
|
||||
)
|
||||
|
||||
#endif /* PCI_DEVICE_H */
|
||||
|
@ -83,3 +83,8 @@ void PCIHost::attach_pci_device(std::string& dev_name, int slot_id)
|
||||
this->pci_register_device(
|
||||
slot_id, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name)));
|
||||
}
|
||||
|
||||
PCIDevice *PCIHost::pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -29,6 +29,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
enum {
|
||||
PCI_CONFIG_DIRECTION = 1,
|
||||
PCI_CONFIG_READ = 0,
|
||||
PCI_CONFIG_WRITE = 1,
|
||||
|
||||
PCI_CONFIG_TYPE = 4,
|
||||
PCI_CONFIG_TYPE_0 = 0,
|
||||
PCI_CONFIG_TYPE_1 = 4,
|
||||
};
|
||||
|
||||
/** PCI config space access details */
|
||||
typedef struct AccessDetails {
|
||||
uint8_t size;
|
||||
uint8_t offset;
|
||||
uint8_t flags;
|
||||
} AccessDetails;
|
||||
|
||||
class PCIDevice; // forward declaration to prevent errors
|
||||
|
||||
class PCIHost {
|
||||
@ -46,6 +63,8 @@ public:
|
||||
|
||||
virtual void attach_pci_device(std::string& dev_name, int slot_id);
|
||||
|
||||
virtual PCIDevice *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
|
||||
|
||||
protected:
|
||||
std::unordered_map<int, PCIDevice*> dev_map;
|
||||
std::vector<PCIDevice*> io_space_devs;
|
||||
|
@ -72,6 +72,32 @@ int MPC106::device_postinit()
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MPC106::cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device)
|
||||
{
|
||||
device = NULL;
|
||||
details.size = size;
|
||||
details.offset = offset & 3;
|
||||
|
||||
bus_num = (this->config_addr >> 8) & 0xFF;
|
||||
dev_num = (this->config_addr >> 19) & 0x1F;
|
||||
fun_num = (this->config_addr >> 16) & 0x07;
|
||||
reg_offs = (this->config_addr >> 24) & 0xFC;
|
||||
|
||||
if (bus_num) {
|
||||
details.flags = PCI_CONFIG_TYPE_1;
|
||||
device = pci_find_device(bus_num, dev_num, fun_num);
|
||||
}
|
||||
else {
|
||||
details.flags = PCI_CONFIG_TYPE_0;
|
||||
if (dev_num == 0 && fun_num == 0) {
|
||||
device = this; // dev_num 0 is assigned to myself
|
||||
}
|
||||
else if (this->dev_map.count(dev_num)) {
|
||||
device = this->dev_map[dev_num];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||
uint32_t result;
|
||||
|
||||
@ -87,7 +113,7 @@ uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||
} else {
|
||||
if (offset >= 0x200000) {
|
||||
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
||||
return pci_read(size);
|
||||
return pci_read(offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,116 +137,83 @@ void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size
|
||||
this->config_addr = value;
|
||||
} else {
|
||||
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
||||
return pci_write(value, size);
|
||||
return pci_write(offset, value, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MPC106::pci_read(uint32_t size) {
|
||||
int bus_num, dev_num, fun_num, reg_offs;
|
||||
|
||||
bus_num = (this->config_addr >> 8) & 0xFF;
|
||||
dev_num = (this->config_addr >> 19) & 0x1F;
|
||||
fun_num = (this->config_addr >> 16) & 0x07;
|
||||
reg_offs = (this->config_addr >> 24) & 0xFC;
|
||||
|
||||
if (bus_num) {
|
||||
LOG_F(
|
||||
ERROR,
|
||||
"%s err: read attempt from non-local PCI bus, config_addr = %x %02x:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), this->config_addr, bus_num, dev_num, fun_num, reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
uint32_t MPC106::pci_read(uint32_t offset, uint32_t size) {
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
AccessDetails details;
|
||||
PCIDevice *device;
|
||||
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||
details.flags |= PCI_CONFIG_READ;
|
||||
if (device) {
|
||||
return pci_cfg_rev_read(device->pci_cfg_read(reg_offs, details), details);
|
||||
}
|
||||
|
||||
if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself
|
||||
return this->pci_cfg_read(reg_offs, size);
|
||||
} else {
|
||||
if (this->dev_map.count(dev_num)) {
|
||||
return this->dev_map[dev_num]->pci_cfg_read(reg_offs, size);
|
||||
} else {
|
||||
LOG_F(
|
||||
ERROR,
|
||||
"%s err: read attempt from non-existing PCI device %02x:%02x.%x @%02x.%c",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||
);
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
LOG_READ_NON_EXISTENT_PCI_DEVICE();
|
||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
}
|
||||
|
||||
void MPC106::pci_write(uint32_t value, uint32_t size) {
|
||||
int bus_num, dev_num, fun_num, reg_offs;
|
||||
|
||||
bus_num = (this->config_addr >> 8) & 0xFF;
|
||||
dev_num = (this->config_addr >> 19) & 0x1F;
|
||||
fun_num = (this->config_addr >> 16) & 0x07;
|
||||
reg_offs = (this->config_addr >> 24) & 0xFC;
|
||||
|
||||
if (bus_num) {
|
||||
LOG_F(
|
||||
ERROR,
|
||||
"%s err: write attempt to non-local PCI bus, config_addr = %x %02x:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), this->config_addr, bus_num, dev_num, fun_num, reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
void MPC106::pci_write(uint32_t offset, uint32_t value, uint32_t size) {
|
||||
int bus_num, dev_num, fun_num;
|
||||
uint8_t reg_offs;
|
||||
AccessDetails details;
|
||||
PCIDevice *device;
|
||||
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||
details.flags |= PCI_CONFIG_WRITE;
|
||||
if (device) {
|
||||
uint32_t oldvalue = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
|
||||
value = pci_cfg_rev_write(oldvalue, details, value);
|
||||
device->pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself
|
||||
this->pci_cfg_write(reg_offs, value, size);
|
||||
} else {
|
||||
if (this->dev_map.count(dev_num)) {
|
||||
this->dev_map[dev_num]->pci_cfg_write(reg_offs, value, size);
|
||||
} else {
|
||||
LOG_F(
|
||||
ERROR,
|
||||
"%s err: write attempt to non-existing PCI device %02x:%02x.%x @%02x.%c = %0*x",
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs,
|
||||
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||
size * 2, BYTESWAP_SIZED(value, size)
|
||||
);
|
||||
}
|
||||
}
|
||||
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
|
||||
}
|
||||
|
||||
uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, uint32_t size) {
|
||||
uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, AccessDetails &details) {
|
||||
#ifdef MPC106_DEBUG
|
||||
LOG_F(9, "read from Grackle register %08X", reg_offs);
|
||||
#endif
|
||||
|
||||
if (reg_offs < 64) {
|
||||
return PCIDevice::pci_cfg_read(reg_offs, size);
|
||||
return PCIDevice::pci_cfg_read(reg_offs, details);
|
||||
}
|
||||
|
||||
return read_mem(&this->my_pci_cfg_hdr[reg_offs], size);
|
||||
uint32_t value = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[reg_offs]);
|
||||
if ((reg_offs >= 0x80 && reg_offs <= 0xA0) || reg_offs == 0xF0) {
|
||||
return value;
|
||||
}
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER_WITH_VALUE();
|
||||
return value;
|
||||
}
|
||||
|
||||
void MPC106::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) {
|
||||
void MPC106::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details) {
|
||||
#ifdef MPC106_DEBUG
|
||||
LOG_F(9, "write %08X to Grackle register %08X", value, reg_offs);
|
||||
#endif
|
||||
|
||||
if (reg_offs < 64) {
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, size);
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: implement write-protection for read-only registers
|
||||
|
||||
write_mem(&this->my_pci_cfg_hdr[reg_offs], value, size);
|
||||
uint32_t *addr = (uint32_t *)&this->my_pci_cfg_hdr[reg_offs];
|
||||
WRITE_DWORD_LE_A(addr, value);
|
||||
|
||||
if (this->my_pci_cfg_hdr[0xF2] & 8) {
|
||||
if ((reg_offs >= 0x80 && reg_offs <= 0xA0) || reg_offs == 0xF0) {
|
||||
if (this->my_pci_cfg_hdr[0xF2] & 8) {
|
||||
#ifdef MPC106_DEBUG
|
||||
LOG_F(9, "MPC106: MCCR1[MEMGO] was set!");
|
||||
LOG_F(9, "MPC106: MCCR1[MEMGO] was set!");
|
||||
#endif
|
||||
setup_ram();
|
||||
setup_ram();
|
||||
}
|
||||
return;
|
||||
}
|
||||
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
|
||||
void MPC106::setup_ram() {
|
||||
|
@ -59,12 +59,13 @@ public:
|
||||
|
||||
protected:
|
||||
/* PCI access */
|
||||
uint32_t pci_read(uint32_t size);
|
||||
void pci_write(uint32_t value, uint32_t size);
|
||||
uint32_t pci_read(uint32_t offset, uint32_t size);
|
||||
void pci_write(uint32_t offset, uint32_t value, uint32_t size);
|
||||
void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device);
|
||||
|
||||
/* my own PCI configuration registers access */
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||
|
||||
bool supports_io_space(void) {
|
||||
return true;
|
||||
|
@ -161,34 +161,35 @@ void ATIRage::notify_bar_change(int bar_num)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ATIRage::pci_cfg_read(uint32_t reg_offs, uint32_t size)
|
||||
uint32_t ATIRage::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
return PCIDevice::pci_cfg_read(reg_offs, size);
|
||||
return PCIDevice::pci_cfg_read(reg_offs, details);
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case 0x40:
|
||||
return this->user_cfg;
|
||||
default:
|
||||
LOG_F(WARNING, "ATIRage: reading from unimplemented config register at 0x%X", reg_offs);
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ATIRage::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
void ATIRage::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, size);
|
||||
} else {
|
||||
switch (reg_offs) {
|
||||
case 0x40:
|
||||
this->user_cfg = value;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "ATIRage: writing to unimplemented config register at 0x%X", reg_offs);
|
||||
}
|
||||
PCIDevice::pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case 0x40:
|
||||
this->user_cfg = value;
|
||||
break;
|
||||
default:
|
||||
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ public:
|
||||
return true;
|
||||
};
|
||||
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||
|
||||
/* I/O space access methods */
|
||||
bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res);
|
||||
|
Loading…
Reference in New Issue
Block a user