mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-25 18:29:49 +00:00
Merge pull request #43 from joevt/PCI-bridge-and-multi-function-support
Pci bridge and multi function support
This commit is contained in:
commit
8231ecd466
@ -139,101 +139,50 @@ void BanditPciDevice::verbose_address_space()
|
|||||||
|
|
||||||
uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
|
uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
|
||||||
{
|
{
|
||||||
uint32_t idsel, result;
|
|
||||||
|
|
||||||
switch (offset >> 22) {
|
switch (offset >> 22) {
|
||||||
case 3: // CONFIG_DATA
|
case 3: // CONFIG_DATA
|
||||||
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
|
int bus_num, dev_num, fun_num;
|
||||||
LOG_F(
|
uint8_t reg_offs;
|
||||||
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x",
|
AccessDetails details;
|
||||||
this->name.c_str(), BUS_NUM(), DEV_NUM(), FUN_NUM(), REG_NUM() + (offset & 3)
|
PCIDevice *device;
|
||||||
);
|
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
details.flags |= PCI_CONFIG_READ;
|
||||||
|
if (device) {
|
||||||
|
return pci_conv_rd_data(device->pci_cfg_read(reg_offs, details), details);
|
||||||
}
|
}
|
||||||
|
LOG_READ_NON_EXISTENT_PCI_DEVICE();
|
||||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
|
||||||
|
|
||||||
if (!SINGLE_BIT_SET(idsel)) {
|
|
||||||
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
|
|
||||||
this->name.c_str(), (uint32_t)this->config_addr);
|
|
||||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||||
}
|
|
||||||
|
|
||||||
if (this->dev_map.count(idsel)) {
|
|
||||||
AccessDetails details;
|
|
||||||
details.offset = offset & 3;
|
|
||||||
details.size = size;
|
|
||||||
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_READ;
|
|
||||||
|
|
||||||
result = this->dev_map[idsel]->pci_cfg_read(REG_NUM(), details);
|
|
||||||
return pci_conv_rd_data(result, details);
|
|
||||||
} else {
|
|
||||||
LOG_F(
|
|
||||||
ERROR, "%s err: read attempt from non-existing PCI device ??:%02x.%x @%02x",
|
|
||||||
this->name.c_str(), WHAT_BIT_SET(idsel) + 11, FUN_NUM(), REG_NUM() + (offset & 3)
|
|
||||||
);
|
|
||||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // CONFIG_ADDR
|
case 2: // CONFIG_ADDR
|
||||||
return BYTESWAP_32(this->config_addr);
|
return BYTESWAP_32(this->config_addr);
|
||||||
|
|
||||||
default: // I/O space
|
default: // I/O space
|
||||||
// broadcast I/O request to devices that support I/O space
|
return pci_io_read_broadcast(offset, size);
|
||||||
// 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);
|
|
||||||
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BanditHost::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)
|
||||||
{
|
{
|
||||||
uint32_t idsel;
|
|
||||||
|
|
||||||
switch (offset >> 22) {
|
switch (offset >> 22) {
|
||||||
case 3: // CONFIG_DATA
|
case 3: // CONFIG_DATA
|
||||||
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
|
int bus_num, dev_num, fun_num;
|
||||||
LOG_F(
|
uint8_t reg_offs;
|
||||||
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x",
|
AccessDetails details;
|
||||||
this->name.c_str(), BUS_NUM(), DEV_NUM(), FUN_NUM(), REG_NUM() + (offset & 3)
|
PCIDevice *device;
|
||||||
);
|
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||||
return;
|
details.flags |= PCI_CONFIG_WRITE;
|
||||||
}
|
if (device) {
|
||||||
|
|
||||||
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
|
|
||||||
|
|
||||||
if (!SINGLE_BIT_SET(idsel)) {
|
|
||||||
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
|
|
||||||
this->name.c_str(), (uint32_t)this->config_addr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->dev_map.count(idsel)) {
|
|
||||||
AccessDetails details;
|
|
||||||
details.offset = offset & 3;
|
|
||||||
details.size = size;
|
|
||||||
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_WRITE;
|
|
||||||
|
|
||||||
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
|
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
|
||||||
this->dev_map[idsel]->pci_cfg_write(REG_NUM(), BYTESWAP_32(value), details);
|
device->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
|
||||||
} else { // otherwise perform necessary data transformations -> slow path
|
return;
|
||||||
uint32_t old_val = this->dev_map[idsel]->pci_cfg_read(REG_NUM(), details);
|
|
||||||
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
|
|
||||||
this->dev_map[idsel]->pci_cfg_write(REG_NUM(), new_val, details);
|
|
||||||
}
|
}
|
||||||
} else {
|
// otherwise perform necessary data transformations -> slow path
|
||||||
LOG_F(
|
uint32_t old_val = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
|
||||||
ERROR, "%s err: write attempt to non-existing PCI device ??:%02x.%x @%02x",
|
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
|
||||||
this->name.c_str(), WHAT_BIT_SET(idsel) + 11, FUN_NUM(), REG_NUM() + (offset & 3)
|
device->pci_cfg_write(reg_offs, new_val, details);
|
||||||
);
|
return;
|
||||||
}
|
}
|
||||||
|
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // CONFIG_ADDR
|
case 2: // CONFIG_ADDR
|
||||||
@ -241,14 +190,35 @@ void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default: // I/O space
|
default: // I/O space
|
||||||
// broadcast I/O request to devices that support I/O space
|
pci_io_write_broadcast(offset, size, value);
|
||||||
// until a device returns true that means "request handled"
|
}
|
||||||
for (auto& dev : this->io_space_devs) {
|
}
|
||||||
if (dev->pci_io_write(offset, value, size))
|
|
||||||
return;
|
inline 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)
|
||||||
}
|
{
|
||||||
LOG_F(ERROR, "%s: attempt to read from unmapped PCI I/O space, offset=0x%X",
|
device = NULL;
|
||||||
this->name.c_str(), offset);
|
details.size = size;
|
||||||
|
details.offset = offset & 3;
|
||||||
|
fun_num = FUN_NUM();
|
||||||
|
reg_offs = REG_NUM();
|
||||||
|
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
|
||||||
|
details.flags = PCI_CONFIG_TYPE_1;
|
||||||
|
bus_num = BUS_NUM();
|
||||||
|
dev_num = DEV_NUM();
|
||||||
|
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(DEV_FUN(dev_num, fun_num))) {
|
||||||
|
device = this->dev_map[DEV_FUN(dev_num, fun_num)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +246,7 @@ Bandit::Bandit(int bridge_num, std::string name, int dev_id, int rev)
|
|||||||
this->my_pci_device = unique_ptr<BanditPciDevice>(
|
this->my_pci_device = unique_ptr<BanditPciDevice>(
|
||||||
new BanditPciDevice(bridge_num, name, dev_id, rev)
|
new BanditPciDevice(bridge_num, name, dev_id, rev)
|
||||||
);
|
);
|
||||||
this->pci_register_device(1, this->my_pci_device.get());
|
this->pci_register_device(DEV_FUN(BANDIT_DEV,0), this->my_pci_device.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Chaos::Chaos(std::string name) : BanditHost()
|
Chaos::Chaos(std::string name) : BanditHost()
|
||||||
|
@ -46,6 +46,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define BANDIT_DEV (11) // Bandit's own device number
|
||||||
#define BANDIT_CAR_TYPE (1 << 0) // Bandit config address type bit
|
#define BANDIT_CAR_TYPE (1 << 0) // Bandit config address type bit
|
||||||
|
|
||||||
/* Convenient macros for parsing CONFIG_ADDR fields. */
|
/* Convenient macros for parsing CONFIG_ADDR fields. */
|
||||||
@ -75,6 +76,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t config_addr;
|
uint32_t config_addr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
168
devices/common/pci/pcibridge.cpp
Normal file
168
devices/common/pci/pcibridge.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||||
|
Copyright (C) 2018-22 divingkatae and maximum
|
||||||
|
(theweirdo) spatium
|
||||||
|
|
||||||
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <devices/common/pci/pcibridge.h>
|
||||||
|
#include <memaccess.h>
|
||||||
|
|
||||||
|
PCIBridge::PCIBridge(std::string name) : PCIDevice(name)
|
||||||
|
{
|
||||||
|
this->num_bars = 2;
|
||||||
|
this->hdr_type = 1;
|
||||||
|
|
||||||
|
this->pci_rd_primary_bus = [this]() { return this->primary_bus; };
|
||||||
|
this->pci_rd_secondary_bus = [this]() { return this->secondary_bus; };
|
||||||
|
this->pci_rd_subordinate_bus = [this]() { return this->subordinate_bus; };
|
||||||
|
this->pci_rd_sec_latency_timer = [this]() { return this->sec_latency_timer; };
|
||||||
|
this->pci_rd_sec_status = [this]() { return this->sec_status; };
|
||||||
|
this->pci_rd_memory_base = [this]() { return this->memory_base; };
|
||||||
|
this->pci_rd_memory_limit = [this]() { return this->memory_limit; };
|
||||||
|
this->pci_rd_io_base = [this]() { return this->io_base; };
|
||||||
|
this->pci_rd_io_limit = [this]() { return this->io_limit; };
|
||||||
|
this->pci_rd_pref_mem_base = [this]() { return this->pref_mem_base; };
|
||||||
|
this->pci_rd_pref_mem_limit = [this]() { return this->pref_mem_limit; };
|
||||||
|
this->pci_rd_io_base_upper16 = [this]() { return this->io_base_upper16; };
|
||||||
|
this->pci_rd_io_limit_upper16 = [this]() { return this->io_limit_upper16; };
|
||||||
|
this->pci_rd_pref_base_upper32 = [this]() { return this->pref_base_upper32; };
|
||||||
|
this->pci_rd_pref_limit_upper32 = [this]() { return this->pref_limit_upper32; };
|
||||||
|
this->pci_rd_bridge_control = [this]() { return this->bridge_control; };
|
||||||
|
|
||||||
|
this->pci_wr_primary_bus = [this](uint8_t val) { this->primary_bus = val; };
|
||||||
|
this->pci_wr_secondary_bus = [this](uint8_t val) { this->secondary_bus = val; };
|
||||||
|
this->pci_wr_subordinate_bus = [this](uint8_t val) { this->subordinate_bus = val; };
|
||||||
|
this->pci_wr_sec_latency_timer = [this](uint8_t val) { this->sec_latency_timer = (this->sec_latency_timer & ~this->sec_latency_timer_cfg) | (val & this->sec_latency_timer_cfg); };
|
||||||
|
this->pci_wr_sec_status = [this](uint16_t val) {};
|
||||||
|
this->pci_wr_memory_base = [this](uint16_t val) { this->memory_base = (val & this->memory_cfg ) | (this->memory_cfg & 15); this->memory_base_32 = ((this->memory_base & 0xfff0) << 16) ; };
|
||||||
|
this->pci_wr_memory_limit = [this](uint16_t val) { this->memory_limit = (val & this->memory_cfg ) | (this->memory_cfg & 15); this->memory_limit_32 = ((this->memory_limit & 0xfff0) << 16) + 0x100000; };
|
||||||
|
this->pci_wr_io_base = [this](uint8_t val) { this->io_base = (val & this->io_cfg ) | (this->io_cfg & 15); this->io_base_32 = ((uint32_t)this->io_base_upper16 << 16) | ((this->io_base & 0xf0) << 8) ; };
|
||||||
|
this->pci_wr_io_limit = [this](uint8_t val) { this->io_limit = (val & this->io_cfg ) | (this->io_cfg & 15); this->io_limit_32 = (((uint32_t)this->io_limit_upper16 << 16) | ((this->io_limit & 0xf0) << 8)) + 0x1000 ; };
|
||||||
|
this->pci_wr_pref_mem_base = [this](uint16_t val) { this->pref_mem_base = (val & this->pref_mem_cfg) | (this->pref_mem_cfg & 15); this->pref_mem_base_64 = ((uint64_t)this->pref_base_upper32 << 32) | ((this->pref_mem_base & 0xfff0) << 16) ; };
|
||||||
|
this->pci_wr_pref_mem_limit = [this](uint16_t val) { this->pref_mem_limit = (val & this->pref_mem_cfg) | (this->pref_mem_cfg & 15); this->pref_mem_limit_64 = (((uint64_t)this->pref_limit_upper32 << 32) | ((this->pref_mem_limit & 0xfff0) << 16)) + 0x100000; };
|
||||||
|
this->pci_wr_io_base_upper16 = [this](uint16_t val) { if ((this->io_base & 15) == 1) this->io_base_upper16 = val; { this->io_base_32 = ((uint32_t)this->io_base_upper16 << 16) | ((this->io_base & 0xf0) << 8) ; } };
|
||||||
|
this->pci_wr_io_limit_upper16 = [this](uint16_t val) { if ((this->io_limit & 15) == 1) this->io_limit_upper16 = val; { this->io_limit_32 = (((uint32_t)this->io_limit_upper16 << 16) | ((this->io_limit & 0xf0) << 8)) + 0x1000 ; } };
|
||||||
|
this->pci_wr_pref_base_upper32 = [this](uint32_t val) { if ((this->pref_mem_cfg & 15) == 1) this->pref_base_upper32 = val; { this->pref_mem_base_64 = ((uint64_t)this->pref_base_upper32 << 32) | ((this->pref_mem_base & 0xfff0) << 16) ; } };
|
||||||
|
this->pci_wr_pref_limit_upper32 = [this](uint32_t val) { if ((this->pref_mem_cfg & 15) == 1) this->pref_limit_upper32 = val; { this->pref_mem_limit_64 = (((uint64_t)this->pref_limit_upper32 << 32) | ((this->pref_mem_limit & 0xfff0) << 16)) + 0x100000; } };
|
||||||
|
this->pci_wr_bridge_control = [this](uint16_t val) { this->bridge_control = val; };
|
||||||
|
};
|
||||||
|
|
||||||
|
bool PCIBridge::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
|
||||||
|
{
|
||||||
|
// FIXME: constrain region to memory range
|
||||||
|
return this->host_instance->pci_register_mmio_region(start_addr, size, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PCIBridge::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
|
||||||
|
{
|
||||||
|
return this->host_instance->pci_unregister_mmio_region(start_addr, size, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PCIBridge::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
|
||||||
|
{
|
||||||
|
if (reg_offs < 0x18) {
|
||||||
|
return PCIDevice::pci_cfg_read(reg_offs, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg_offs) {
|
||||||
|
case PCI_CFG_PRIMARY_BUS:
|
||||||
|
return (this->pci_rd_sec_latency_timer() << 24) | (this->pci_rd_subordinate_bus() << 16) | (this->pci_rd_secondary_bus() << 8) | (this->pci_rd_primary_bus());
|
||||||
|
case PCI_CFG_IO_BASE:
|
||||||
|
return (this->pci_rd_sec_status() << 16) | (this->pci_rd_io_limit() << 8) | (this->pci_rd_io_base());
|
||||||
|
case PCI_CFG_MEMORY_BASE:
|
||||||
|
return (this->pci_rd_memory_limit() << 16) | (this->pci_rd_memory_base());
|
||||||
|
case PCI_CFG_PREF_MEM_BASE:
|
||||||
|
return (this->pci_rd_pref_mem_limit() << 16) | (this->pci_rd_pref_mem_base());
|
||||||
|
case PCI_CFG_PREF_BASE_UPPER32:
|
||||||
|
return this->pci_rd_pref_base_upper32();
|
||||||
|
case PCI_CFG_PREF_LIMIT_UPPER32:
|
||||||
|
return this->pci_rd_pref_limit_upper32();
|
||||||
|
case PCI_CFG_IO_BASE_UPPER16:
|
||||||
|
return (this->pci_rd_io_limit_upper16() << 16) | (this->pci_rd_io_base_upper16());
|
||||||
|
case PCI_CFG_CAP_PTR:
|
||||||
|
return cap_ptr;
|
||||||
|
case PCI_CFG_BRIDGE_ROM_ADDRESS:
|
||||||
|
return exp_rom_bar;
|
||||||
|
case PCI_CFG_INTERRUPT_LINE:
|
||||||
|
return (this->pci_rd_bridge_control() << 16) | (irq_pin << 8) | irq_line;
|
||||||
|
}
|
||||||
|
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCIBridge::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
|
||||||
|
{
|
||||||
|
if (reg_offs < 0x18) {
|
||||||
|
return PCIDevice::pci_cfg_write(reg_offs, value, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg_offs) {
|
||||||
|
case PCI_CFG_PRIMARY_BUS:
|
||||||
|
this->pci_wr_sec_latency_timer(value >> 24);
|
||||||
|
this->pci_wr_subordinate_bus(value >> 16);
|
||||||
|
this->pci_wr_secondary_bus(value >> 8);
|
||||||
|
this->pci_wr_primary_bus(value & 0xFFU);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_IO_BASE:
|
||||||
|
this->pci_wr_sec_status(value >> 16);
|
||||||
|
this->pci_wr_io_limit(value >> 8);
|
||||||
|
this->pci_wr_io_base(value & 0xFFU);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_MEMORY_BASE:
|
||||||
|
this->pci_wr_memory_limit(value >> 16);
|
||||||
|
this->pci_wr_memory_base(value & 0xFFFFU);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_PREF_MEM_BASE:
|
||||||
|
this->pci_wr_pref_mem_limit(value >> 16);
|
||||||
|
this->pci_wr_pref_mem_base(value & 0xFFFFU);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_PREF_BASE_UPPER32:
|
||||||
|
this->pci_wr_pref_base_upper32(value);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_PREF_LIMIT_UPPER32:
|
||||||
|
this->pci_wr_pref_limit_upper32(value);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_IO_BASE_UPPER16:
|
||||||
|
this->pci_wr_io_limit_upper16(value >> 16);
|
||||||
|
this->pci_wr_io_base_upper16(value & 0xFFFFU);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_BRIDGE_ROM_ADDRESS:
|
||||||
|
this->pci_wr_exp_rom_bar(value);
|
||||||
|
break;
|
||||||
|
case PCI_CFG_INTERRUPT_LINE:
|
||||||
|
this->irq_line = value >> 24;
|
||||||
|
this->pci_wr_bridge_control(value >> 16);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PCIBridge::pci_io_read(uint32_t offset, uint32_t size, uint32_t* res)
|
||||||
|
{
|
||||||
|
if (!(this->command & 1)) return false;
|
||||||
|
if (offset < this->io_base_32 || offset + size >= this->io_limit_32) return false;
|
||||||
|
return this->pci_io_read_loop(offset, size, *res);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PCIBridge::pci_io_write(uint32_t offset, uint32_t value, uint32_t size)
|
||||||
|
{
|
||||||
|
if (!(this->command & 1)) return false;
|
||||||
|
if (offset < this->io_base_32 || offset + size >= this->io_limit_32) return false;
|
||||||
|
return this->pci_io_read_loop(offset, size, value);
|
||||||
|
}
|
139
devices/common/pci/pcibridge.h
Normal file
139
devices/common/pci/pcibridge.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||||
|
Copyright (C) 2018-22 divingkatae and maximum
|
||||||
|
(theweirdo) spatium
|
||||||
|
|
||||||
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PCI_BRIDGE_H
|
||||||
|
#define PCI_BRIDGE_H
|
||||||
|
|
||||||
|
#include <devices/deviceregistry.h>
|
||||||
|
#include <devices/common/pci/pcidevice.h>
|
||||||
|
#include <devices/common/pci/pcihost.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/** PCI configuration space registers offsets */
|
||||||
|
enum {
|
||||||
|
PCI_CFG_PRIMARY_BUS = 0x18, // PRIMARY_BUS, SECONDARY_BUS, SUBORDINATE_BUS, SEC_LATENCY_TIMER
|
||||||
|
PCI_CFG_IO_BASE = 0x1C, // IO_BASE.b, IO_LIMIT.b, SEC_STATUS.w
|
||||||
|
PCI_CFG_MEMORY_BASE = 0x20, // MEMORY_BASE.w, MEMORY_LIMIT.w
|
||||||
|
PCI_CFG_PREF_MEM_BASE = 0x24, // PREF_MEMORY_BASE.w, PREF_MEMORY_LIMIT.w
|
||||||
|
PCI_CFG_PREF_BASE_UPPER32 = 0x28, // PREF_BASE_UPPER32
|
||||||
|
PCI_CFG_PREF_LIMIT_UPPER32 = 0x2c, // PREF_LIMIT_UPPER32
|
||||||
|
PCI_CFG_IO_BASE_UPPER16 = 0x30, // IO_BASE_UPPER16.w, IO_LIMIT_UPPER16.w
|
||||||
|
// PCI_CFG_CAP_PTR
|
||||||
|
PCI_CFG_BRIDGE_ROM_ADDRESS = 0x38, // BRIDGE_ROM_ADDRESS
|
||||||
|
PCI_CFG_INTERRUPT_LINE = 0x3C, // INTERRUPT_LINE.b, INTERRUPT_PIN.b, BRIDGE_CONTROL.w
|
||||||
|
};
|
||||||
|
|
||||||
|
class PCIBridge : public PCIHost, public PCIDevice {
|
||||||
|
friend class PCIHost;
|
||||||
|
public:
|
||||||
|
PCIBridge(std::string name);
|
||||||
|
~PCIBridge() = default;
|
||||||
|
|
||||||
|
// PCIHost methods
|
||||||
|
virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
||||||
|
virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
||||||
|
|
||||||
|
// PCIDevice methods
|
||||||
|
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);
|
||||||
|
|
||||||
|
virtual bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res);
|
||||||
|
virtual bool pci_io_write(uint32_t offset, uint32_t value, uint32_t size);
|
||||||
|
|
||||||
|
// 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) { };
|
||||||
|
|
||||||
|
// plugin interface for using in the derived classes
|
||||||
|
std::function<uint8_t()> pci_rd_primary_bus;
|
||||||
|
std::function<void(uint8_t)> pci_wr_primary_bus;
|
||||||
|
std::function<uint8_t()> pci_rd_secondary_bus;
|
||||||
|
std::function<void(uint8_t)> pci_wr_secondary_bus;
|
||||||
|
std::function<uint8_t()> pci_rd_subordinate_bus;
|
||||||
|
std::function<void(uint8_t)> pci_wr_subordinate_bus;
|
||||||
|
std::function<uint8_t()> pci_rd_sec_latency_timer;
|
||||||
|
std::function<void(uint8_t)> pci_wr_sec_latency_timer;
|
||||||
|
std::function<uint16_t()> pci_rd_sec_status;
|
||||||
|
std::function<void(uint16_t)> pci_wr_sec_status;
|
||||||
|
std::function<uint8_t()> pci_rd_io_base;
|
||||||
|
std::function<void(uint8_t)> pci_wr_io_base;
|
||||||
|
std::function<uint8_t()> pci_rd_io_limit;
|
||||||
|
std::function<void(uint8_t)> pci_wr_io_limit;
|
||||||
|
std::function<uint16_t()> pci_rd_memory_base;
|
||||||
|
std::function<void(uint16_t)> pci_wr_memory_base;
|
||||||
|
std::function<uint16_t()> pci_rd_memory_limit;
|
||||||
|
std::function<void(uint16_t)> pci_wr_memory_limit;
|
||||||
|
std::function<uint16_t()> pci_rd_pref_mem_base;
|
||||||
|
std::function<void(uint16_t)> pci_wr_pref_mem_base;
|
||||||
|
std::function<uint16_t()> pci_rd_pref_mem_limit;
|
||||||
|
std::function<void(uint16_t)> pci_wr_pref_mem_limit;
|
||||||
|
std::function<uint32_t()> pci_rd_pref_base_upper32;
|
||||||
|
std::function<void(uint32_t)> pci_wr_pref_base_upper32;
|
||||||
|
std::function<uint32_t()> pci_rd_pref_limit_upper32;
|
||||||
|
std::function<void(uint32_t)> pci_wr_pref_limit_upper32;
|
||||||
|
std::function<uint16_t()> pci_rd_io_base_upper16;
|
||||||
|
std::function<void(uint16_t)> pci_wr_io_base_upper16;
|
||||||
|
std::function<uint16_t()> pci_rd_io_limit_upper16;
|
||||||
|
std::function<void(uint16_t)> pci_wr_io_limit_upper16;
|
||||||
|
std::function<uint16_t()> pci_rd_bridge_control;
|
||||||
|
std::function<void(uint16_t)> pci_wr_bridge_control;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// PCI configuration space state
|
||||||
|
uint8_t primary_bus = 0;
|
||||||
|
uint8_t secondary_bus = 0;
|
||||||
|
uint8_t subordinate_bus = 0;
|
||||||
|
uint8_t sec_latency_timer = 0; // if supportss r/w then must reset to 0
|
||||||
|
uint16_t sec_status = 0;
|
||||||
|
uint8_t io_base = 0;
|
||||||
|
uint8_t io_limit = 0;
|
||||||
|
uint16_t memory_base = 0;
|
||||||
|
uint16_t memory_limit = 0;
|
||||||
|
uint16_t pref_mem_base = 0;
|
||||||
|
uint16_t pref_mem_limit = 0;
|
||||||
|
uint32_t pref_base_upper32 = 0;
|
||||||
|
uint32_t pref_limit_upper32 = 0;
|
||||||
|
uint16_t io_base_upper16 = 0;
|
||||||
|
uint16_t io_limit_upper16 = 0;
|
||||||
|
uint16_t bridge_control = 0;
|
||||||
|
|
||||||
|
uint8_t sec_latency_timer_cfg = 0; // 0 = not writable, 0xf8 = limits the granularity to eight PCI clocks
|
||||||
|
uint8_t io_cfg = 0xf0; // 0 = not writable, 0xf0 = supports 16 bit io range, 0xf1 = supports 32 bit io range
|
||||||
|
uint16_t memory_cfg = 0xfff0; // 0 = not writable, 0xfff0 = supports 32 bit memory range
|
||||||
|
uint16_t pref_mem_cfg = 0xfff0; // 0 = not writable, 0xfff0 = supports 32 bit prefetchable memory range, 0xfff1 = supports 64 bit prefetchable memory range
|
||||||
|
|
||||||
|
uint32_t io_base_32 = 0;
|
||||||
|
uint32_t io_limit_32 = 0;
|
||||||
|
uint64_t memory_base_32 = 0;
|
||||||
|
uint64_t memory_limit_32 = 0;
|
||||||
|
uint64_t pref_mem_base_64 = 0;
|
||||||
|
uint64_t pref_mem_limit_64 = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PCI_BRIDGE_H */
|
@ -20,7 +20,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <devices/common/pci/pcidevice.h>
|
#include <devices/common/pci/pcidevice.h>
|
||||||
#include <devices/common/viacuda.h>
|
|
||||||
#include <endianswap.h>
|
#include <endianswap.h>
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
#include <memaccess.h>
|
#include <memaccess.h>
|
||||||
@ -103,17 +102,7 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &
|
|||||||
this->set_bar_value((reg_offs - 0x10) >> 2, value);
|
this->set_bar_value((reg_offs - 0x10) >> 2, value);
|
||||||
break;
|
break;
|
||||||
case PCI_CFG_ROM_BAR:
|
case PCI_CFG_ROM_BAR:
|
||||||
if ((value & this->exp_bar_cfg) == this->exp_bar_cfg) {
|
this->pci_wr_exp_rom_bar(value);
|
||||||
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
|
|
||||||
} else {
|
|
||||||
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
|
|
||||||
if (this->exp_rom_bar & 1) {
|
|
||||||
this->map_exp_rom_mem();
|
|
||||||
} else {
|
|
||||||
LOG_F(WARNING, "%s: unmapping of expansion ROM not implemented yet",
|
|
||||||
this->pci_name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PCI_CFG_DWORD_15:
|
case PCI_CFG_DWORD_15:
|
||||||
this->irq_line = value >> 24;
|
this->irq_line = value >> 24;
|
||||||
@ -242,7 +231,7 @@ void PCIDevice::set_bar_value(int bar_num, uint32_t value)
|
|||||||
|
|
||||||
void PCIDevice::finish_config_bars()
|
void PCIDevice::finish_config_bars()
|
||||||
{
|
{
|
||||||
for (int bar_num = 0; bar_num < 6; bar_num++) {
|
for (int bar_num = 0; bar_num < this->num_bars; bar_num++) {
|
||||||
uint32_t bar_cfg = this->bars_cfg[bar_num];
|
uint32_t bar_cfg = this->bars_cfg[bar_num];
|
||||||
|
|
||||||
if (!bar_cfg) // skip unimplemented BARs
|
if (!bar_cfg) // skip unimplemented BARs
|
||||||
@ -251,6 +240,7 @@ void PCIDevice::finish_config_bars()
|
|||||||
if (bar_cfg & 1) {
|
if (bar_cfg & 1) {
|
||||||
bars_typ[bar_num] = (bar_cfg & 0xffff0000) ? PCIBarType::Io_32_Bit :
|
bars_typ[bar_num] = (bar_cfg & 0xffff0000) ? PCIBarType::Io_32_Bit :
|
||||||
PCIBarType::Io_16_Bit;
|
PCIBarType::Io_16_Bit;
|
||||||
|
has_io_space = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int pci_space_type = (bar_cfg >> 1) & 3;
|
int pci_space_type = (bar_cfg >> 1) & 3;
|
||||||
@ -262,7 +252,7 @@ void PCIDevice::finish_config_bars()
|
|||||||
bars_typ[bar_num] = PCIBarType::Mem_20_Bit;
|
bars_typ[bar_num] = PCIBarType::Mem_20_Bit;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (bar_num >= 5) {
|
if (bar_num >= num_bars - 1) {
|
||||||
ABORT_F("%s: BAR %d cannot be 64-bit",
|
ABORT_F("%s: BAR %d cannot be 64-bit",
|
||||||
this->pci_name.c_str(), bar_num);
|
this->pci_name.c_str(), bar_num);
|
||||||
}
|
}
|
||||||
@ -271,8 +261,8 @@ void PCIDevice::finish_config_bars()
|
|||||||
this->pci_name.c_str(), bar_num);
|
this->pci_name.c_str(), bar_num);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bars_typ[bar_num] = PCIBarType::Mem_64_Bit_Hi;
|
|
||||||
bars_typ[bar_num++] = PCIBarType::Mem_64_Bit_Lo;
|
bars_typ[bar_num++] = PCIBarType::Mem_64_Bit_Lo;
|
||||||
|
bars_typ[bar_num ] = PCIBarType::Mem_64_Bit_Hi;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -285,13 +275,45 @@ void PCIDevice::finish_config_bars()
|
|||||||
|
|
||||||
void PCIDevice::map_exp_rom_mem()
|
void PCIDevice::map_exp_rom_mem()
|
||||||
{
|
{
|
||||||
uint32_t rom_addr, rom_size;
|
uint32_t rom_addr = this->exp_rom_bar & this->exp_bar_cfg;
|
||||||
|
if (rom_addr) {
|
||||||
rom_addr = this->exp_rom_bar & this->exp_bar_cfg;
|
if (this->exp_rom_addr != rom_addr) {
|
||||||
rom_size = ~this->exp_bar_cfg + 1;
|
this->unmap_exp_rom_mem();
|
||||||
|
uint32_t rom_size = ~this->exp_bar_cfg + 1;
|
||||||
if (!this->exp_rom_addr || this->exp_rom_addr != rom_addr) {
|
|
||||||
this->host_instance->pci_register_mmio_region(rom_addr, rom_size, this);
|
this->host_instance->pci_register_mmio_region(rom_addr, rom_size, this);
|
||||||
this->exp_rom_addr = rom_addr;
|
this->exp_rom_addr = rom_addr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->unmap_exp_rom_mem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCIDevice::unmap_exp_rom_mem()
|
||||||
|
{
|
||||||
|
if (this->exp_rom_addr) {
|
||||||
|
uint32_t rom_size = ~this->exp_bar_cfg + 1;
|
||||||
|
this->host_instance->pci_unregister_mmio_region(exp_rom_addr, rom_size, this);
|
||||||
|
this->exp_rom_addr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCIDevice::pci_wr_exp_rom_bar(uint32_t data)
|
||||||
|
{
|
||||||
|
if (!this->exp_bar_cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data & this->exp_bar_cfg) == this->exp_bar_cfg) {
|
||||||
|
// doing sizing
|
||||||
|
this->exp_rom_bar = (data & (this->exp_bar_cfg | 1));
|
||||||
|
} else {
|
||||||
|
this->exp_rom_bar = (data & (this->exp_bar_cfg | 1));
|
||||||
|
if (this->exp_rom_bar & 1) {
|
||||||
|
this->map_exp_rom_mem();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->unmap_exp_rom_mem();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public:
|
|||||||
virtual ~PCIDevice() = default;
|
virtual ~PCIDevice() = default;
|
||||||
|
|
||||||
virtual bool supports_io_space() {
|
virtual bool supports_io_space() {
|
||||||
return false;
|
return has_io_space;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* I/O space access methods */
|
/* I/O space access methods */
|
||||||
@ -117,6 +117,13 @@ public:
|
|||||||
this->host_instance = host_instance;
|
this->host_instance = host_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtual void set_multi_function(bool is_multi_function) {
|
||||||
|
this->hdr_type = is_multi_function ? (this->hdr_type | 0x80) : (this->hdr_type & 0x7f);
|
||||||
|
}
|
||||||
|
virtual void set_irq_pin(uint8_t irq_pin) {
|
||||||
|
this->irq_pin = irq_pin;
|
||||||
|
}
|
||||||
|
|
||||||
// MMIODevice methods
|
// MMIODevice methods
|
||||||
virtual uint32_t read(uint32_t rgn_start, uint32_t offset, int size) { return 0; }
|
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) { }
|
virtual void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) { }
|
||||||
@ -125,7 +132,9 @@ protected:
|
|||||||
void set_bar_value(int bar_num, uint32_t value);
|
void set_bar_value(int bar_num, uint32_t value);
|
||||||
void setup_bars(std::vector<BarConfig> cfg_data);
|
void setup_bars(std::vector<BarConfig> cfg_data);
|
||||||
void finish_config_bars();
|
void finish_config_bars();
|
||||||
|
void pci_wr_exp_rom_bar(uint32_t data);
|
||||||
void map_exp_rom_mem();
|
void map_exp_rom_mem();
|
||||||
|
void unmap_exp_rom_mem();
|
||||||
|
|
||||||
std::string pci_name; // human-readable device name
|
std::string pci_name; // human-readable device name
|
||||||
PCIHost* host_instance; // host bridge instance to call back
|
PCIHost* host_instance; // host bridge instance to call back
|
||||||
@ -136,7 +145,7 @@ protected:
|
|||||||
uint32_t class_rev; // class code and revision id
|
uint32_t class_rev; // class code and revision id
|
||||||
uint16_t status = 0;
|
uint16_t status = 0;
|
||||||
uint16_t command = 0;
|
uint16_t command = 0;
|
||||||
uint8_t hdr_type = 0; // header type
|
uint8_t hdr_type = 0; // header type, single function
|
||||||
uint8_t lat_timer = 0; // latency timer
|
uint8_t lat_timer = 0; // latency timer
|
||||||
uint8_t cache_ln_sz = 0; // cache line size
|
uint8_t cache_ln_sz = 0; // cache line size
|
||||||
uint16_t subsys_id = 0;
|
uint16_t subsys_id = 0;
|
||||||
@ -147,6 +156,8 @@ protected:
|
|||||||
uint8_t irq_pin = 0;
|
uint8_t irq_pin = 0;
|
||||||
uint8_t irq_line = 0;
|
uint8_t irq_line = 0;
|
||||||
|
|
||||||
|
bool has_io_space = false;
|
||||||
|
int num_bars = 6;
|
||||||
uint32_t bars[6] = { 0 }; // base address registers
|
uint32_t bars[6] = { 0 }; // base address registers
|
||||||
uint32_t bars_cfg[6] = { 0 }; // configuration values for base address registers
|
uint32_t bars_cfg[6] = { 0 }; // configuration values for base address registers
|
||||||
PCIBarType bars_typ[6] = { PCIBarType::Unused }; // types for base address registers
|
PCIBarType bars_typ[6] = { PCIBarType::Unused }; // types for base address registers
|
||||||
|
@ -20,28 +20,50 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <devices/common/hwcomponent.h>
|
#include <devices/common/hwcomponent.h>
|
||||||
#include <devices/common/pci/pcidevice.h>
|
#include <devices/common/pci/pcibridge.h>
|
||||||
#include <devices/common/pci/pcihost.h>
|
#include <devices/common/pci/pcihost.h>
|
||||||
#include <devices/memctrl/memctrlbase.h>
|
#include <devices/memctrl/memctrlbase.h>
|
||||||
#include <machines/machinebase.h>
|
#include <machines/machinebase.h>
|
||||||
|
#include <endianswap.h>
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
bool PCIHost::pci_register_device(int dev_num, PCIDevice* dev_instance)
|
bool PCIHost::pci_register_device(int dev_fun_num, PCIDevice* dev_instance)
|
||||||
{
|
{
|
||||||
// return false if dev_num already registered
|
// return false if dev_fun_num already registered
|
||||||
if (this->dev_map.count(dev_num))
|
if (this->dev_map.count(dev_fun_num))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this->dev_map[dev_num] = dev_instance;
|
int fun_num = dev_fun_num & 7;
|
||||||
|
int dev_num = (dev_fun_num >> 3) & 0x1f;
|
||||||
|
bool is_multi_function = fun_num != 0;
|
||||||
|
|
||||||
|
for (int other_fun_num = 0; other_fun_num < 8; other_fun_num++) {
|
||||||
|
if (this->dev_map.count(DEV_FUN(dev_num, other_fun_num))) {
|
||||||
|
is_multi_function = true;
|
||||||
|
if (is_multi_function && other_fun_num == 0) {
|
||||||
|
this->dev_map[DEV_FUN(dev_num, other_fun_num)]->set_multi_function(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->dev_map[dev_fun_num] = dev_instance;
|
||||||
|
|
||||||
dev_instance->set_host(this);
|
dev_instance->set_host(this);
|
||||||
|
if (is_multi_function && fun_num == 0) {
|
||||||
|
dev_instance->set_multi_function(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (dev_instance->supports_io_space()) {
|
if (dev_instance->supports_io_space()) {
|
||||||
this->io_space_devs.push_back(dev_instance);
|
this->io_space_devs.push_back(dev_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PCIBridge *bridge = dynamic_cast<PCIBridge*>(dev_instance);
|
||||||
|
if (bridge) {
|
||||||
|
this->bridge_devs.push_back(bridge);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,30 +83,113 @@ bool PCIHost::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCI
|
|||||||
return mem_ctrl->remove_mmio_region(start_addr, size, obj);
|
return mem_ctrl->remove_mmio_region(start_addr, size, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIHost::attach_pci_device(std::string& dev_name, int slot_id)
|
void PCIHost::attach_pci_device(const std::string& dev_name, int slot_id)
|
||||||
|
{
|
||||||
|
this->attach_pci_device(dev_name, slot_id, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
PCIDevice *PCIHost::attach_pci_device(const std::string& dev_name, int slot_id, const std::string& dev_suffix)
|
||||||
{
|
{
|
||||||
if (!DeviceRegistry::device_registered(dev_name)) {
|
if (!DeviceRegistry::device_registered(dev_name)) {
|
||||||
LOG_F(WARNING, "PCIHost: specified PCI device %s doesn't exist", dev_name.c_str());
|
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
|
||||||
return;
|
LOG_F(
|
||||||
|
WARNING, "%s: specified PCI device %s doesn't exist",
|
||||||
|
hwc ? hwc->get_name().c_str() : "PCIHost", dev_name.c_str()
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to create device object
|
// attempt to create device object
|
||||||
auto dev_obj = DeviceRegistry::get_descriptor(dev_name).m_create_func();
|
auto dev_obj = DeviceRegistry::get_descriptor(dev_name).m_create_func();
|
||||||
|
|
||||||
if (!dev_obj->supports_type(HWCompType::PCI_DEV)) {
|
if (!dev_obj->supports_type(HWCompType::PCI_DEV)) {
|
||||||
LOG_F(WARNING, "PCIHost: cannot attach non-PCI device %s", dev_name.c_str());
|
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
|
||||||
return;
|
LOG_F(
|
||||||
|
WARNING, "%s: cannot attach non-PCI device %s",
|
||||||
|
hwc ? hwc->get_name().c_str() : "PCIHost", dev_name.c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add device to the machine object
|
// add device to the machine object
|
||||||
gMachineObj->add_device(dev_name, std::move(dev_obj));
|
gMachineObj->add_device(dev_name + dev_suffix, std::move(dev_obj));
|
||||||
|
|
||||||
|
PCIDevice *dev = dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name + dev_suffix));
|
||||||
|
|
||||||
// register device with the PCI host
|
// register device with the PCI host
|
||||||
this->pci_register_device(
|
this->pci_register_device(slot_id, dev);
|
||||||
slot_id, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name)));
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PCIHost::pci_io_read_loop(uint32_t offset, int size, uint32_t &res)
|
||||||
|
{
|
||||||
|
for (auto& dev : this->io_space_devs) {
|
||||||
|
if (dev->pci_io_read(offset, size, &res)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PCIHost::pci_io_write_loop(uint32_t offset, int size, uint32_t value)
|
||||||
|
{
|
||||||
|
for (auto& dev : this->io_space_devs) {
|
||||||
|
if (dev->pci_io_write(offset, value, size)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PCIHost::pci_io_read_broadcast(uint32_t offset, int size)
|
||||||
|
{
|
||||||
|
uint32_t res;
|
||||||
|
// broadcast I/O request to devices that support I/O space
|
||||||
|
// until a device returns true that means "request accepted"
|
||||||
|
if (pci_io_read_loop (offset, size, res)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
|
||||||
|
LOG_F(
|
||||||
|
ERROR, "%s: Attempt to read from unmapped PCI I/O space @%08x.%c",
|
||||||
|
hwc ? hwc->get_name().c_str() : "PCIHost", offset,
|
||||||
|
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
|
||||||
|
);
|
||||||
|
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCIHost::pci_io_write_broadcast(uint32_t offset, int size, uint32_t value)
|
||||||
|
{
|
||||||
|
// broadcast I/O request to devices that support I/O space
|
||||||
|
// until a device returns true that means "request accepted"
|
||||||
|
if (pci_io_write_loop(offset, size, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
|
||||||
|
LOG_F(
|
||||||
|
ERROR, "%s: Attempt to write to unmapped PCI I/O space @%08x.%c = %0*x",
|
||||||
|
hwc ? hwc->get_name().c_str() : "PCIHost", offset,
|
||||||
|
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
|
||||||
|
size * 2, BYTESWAP_SIZED(value, size)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PCIDevice *PCIHost::pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num)
|
PCIDevice *PCIHost::pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num)
|
||||||
{
|
{
|
||||||
|
for (auto& bridge : this->bridge_devs) {
|
||||||
|
if (bridge->secondary_bus <= bus_num) {
|
||||||
|
if (bridge->secondary_bus == bus_num) {
|
||||||
|
if (bridge->dev_map.count(DEV_FUN(dev_num, fun_num))) {
|
||||||
|
return bridge->dev_map[DEV_FUN(dev_num, fun_num)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bridge->subordinate_bus >= bus_num) {
|
||||||
|
return bridge->pci_find_device(bus_num, dev_num, fun_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,10 @@ typedef struct AccessDetails {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} AccessDetails;
|
} AccessDetails;
|
||||||
|
|
||||||
class PCIDevice; // forward declaration to prevent errors
|
#define DEV_FUN(dev_num,fun_num) (((dev_num) << 3) | (fun_num))
|
||||||
|
|
||||||
|
class PCIDevice;
|
||||||
|
class PCIBridge;
|
||||||
|
|
||||||
class PCIHost {
|
class PCIHost {
|
||||||
public:
|
public:
|
||||||
@ -58,18 +61,26 @@ public:
|
|||||||
};
|
};
|
||||||
~PCIHost() = default;
|
~PCIHost() = default;
|
||||||
|
|
||||||
virtual bool pci_register_device(int dev_num, PCIDevice* dev_instance);
|
virtual bool pci_register_device(int dev_fun_num, PCIDevice* dev_instance);
|
||||||
|
|
||||||
virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
||||||
virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
|
||||||
|
|
||||||
virtual void attach_pci_device(std::string& dev_name, int slot_id);
|
virtual void attach_pci_device(const std::string& dev_name, int slot_id);
|
||||||
|
PCIDevice *attach_pci_device(const std::string& dev_name, int slot_id, const std::string& dev_suffix);
|
||||||
|
|
||||||
|
virtual bool pci_io_read_loop (uint32_t offset, int size, uint32_t &res);
|
||||||
|
virtual bool pci_io_write_loop(uint32_t offset, int size, uint32_t value);
|
||||||
|
|
||||||
|
virtual uint32_t pci_io_read_broadcast (uint32_t offset, int size);
|
||||||
|
virtual void pci_io_write_broadcast(uint32_t offset, int size, uint32_t value);
|
||||||
|
|
||||||
virtual PCIDevice *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
|
virtual PCIDevice *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unordered_map<int, PCIDevice*> dev_map;
|
std::unordered_map<int, PCIDevice*> dev_map;
|
||||||
std::vector<PCIDevice*> io_space_devs;
|
std::vector<PCIDevice*> io_space_devs;
|
||||||
|
std::vector<PCIBridge*> bridge_devs;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helpers for data conversion in the PCI Configuration space.
|
// Helpers for data conversion in the PCI Configuration space.
|
||||||
|
@ -48,7 +48,7 @@ MPC106::MPC106() : MemCtrlBase(), PCIDevice("Grackle"), PCIHost()
|
|||||||
this->status = 0x80;
|
this->status = 0x80;
|
||||||
|
|
||||||
// assign PCI device number zero to myself
|
// assign PCI device number zero to myself
|
||||||
this->pci_register_device(0, this);
|
this->pci_register_device(DEV_FUN(0,0), this);
|
||||||
|
|
||||||
// add PCI/ISA I/O space, 64K for now
|
// add PCI/ISA I/O space, 64K for now
|
||||||
add_mmio_region(0xFE000000, 0x10000, this);
|
add_mmio_region(0xFE000000, 0x10000, this);
|
||||||
@ -62,7 +62,7 @@ int MPC106::device_postinit()
|
|||||||
std::string pci_dev_name;
|
std::string pci_dev_name;
|
||||||
|
|
||||||
static const std::map<std::string, int> pci_slots = {
|
static const std::map<std::string, int> pci_slots = {
|
||||||
{"pci_A1", 0xD}, {"pci_B1", 0xE}, {"pci_C1", 0xF}
|
{"pci_A1", DEV_FUN(0xD,0)}, {"pci_B1", DEV_FUN(0xE,0)}, {"pci_C1", DEV_FUN(0xF,0)}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& slot : pci_slots) {
|
for (auto& slot : pci_slots) {
|
||||||
@ -75,105 +75,87 @@ int MPC106::device_postinit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
|
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||||
uint32_t result;
|
|
||||||
|
|
||||||
if (rgn_start == 0xFE000000) {
|
if (rgn_start == 0xFE000000) {
|
||||||
// broadcast I/O request to devices that support I/O space
|
return pci_io_read_broadcast(offset, size);
|
||||||
// until a device returns true that means "request accepted"
|
}
|
||||||
for (auto& dev : this->io_space_devs) {
|
if (offset < 0x200000) {
|
||||||
if (dev->pci_io_read(offset, size, &result)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_F(ERROR, "Attempt to read from unmapped PCI I/O space, offset=0x%X", offset);
|
|
||||||
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
|
|
||||||
} else {
|
|
||||||
if (offset >= 0x200000) {
|
|
||||||
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
|
||||||
return pci_read(offset, size);
|
|
||||||
} else {
|
|
||||||
return this->config_addr;
|
return this->config_addr;
|
||||||
}
|
}
|
||||||
|
if (this->config_addr & 0x80) { // process only if bit E (enable) is set
|
||||||
|
return pci_read(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
|
void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
|
||||||
if (rgn_start == 0xFE000000) {
|
if (rgn_start == 0xFE000000) {
|
||||||
// broadcast I/O request to devices that support I/O space
|
pci_io_write_broadcast(offset, size, value);
|
||||||
// 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
LOG_F(ERROR, "Attempt to write to unmapped PCI I/O space, offset=0x%X", offset);
|
|
||||||
} else {
|
|
||||||
if (offset < 0x200000) {
|
if (offset < 0x200000) {
|
||||||
this->config_addr = value;
|
this->config_addr = value;
|
||||||
} else {
|
return;
|
||||||
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
}
|
||||||
|
if (this->config_addr & 0x80) { // process only if bit E (enable) is set
|
||||||
return pci_write(offset, value, size);
|
return pci_write(offset, value, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t MPC106::pci_read(uint32_t offset, uint32_t size) {
|
uint32_t MPC106::pci_read(uint32_t offset, uint32_t size) {
|
||||||
int bus_num = (this->config_addr >> 8) & 0xFF;
|
int bus_num, dev_num, fun_num;
|
||||||
int dev_num = (this->config_addr >> 19) & 0x1F;
|
uint8_t reg_offs;
|
||||||
int fun_num = (this->config_addr >> 16) & 0x07;
|
AccessDetails details;
|
||||||
int reg_offs = (this->config_addr >> 24) & 0xFC;
|
PCIDevice *device;
|
||||||
|
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||||
if (bus_num) {
|
details.flags |= PCI_CONFIG_READ;
|
||||||
LOG_F(ERROR, "%s: read attempt from non-local PCI bus, %02x:%02x.%x @%02x",
|
if (device) {
|
||||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3));
|
return pci_conv_rd_data(device->pci_cfg_read(reg_offs, details), details);
|
||||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
|
||||||
}
|
}
|
||||||
|
LOG_READ_NON_EXISTENT_PCI_DEVICE();
|
||||||
if (this->dev_map.count(dev_num)) {
|
|
||||||
AccessDetails details;
|
|
||||||
details.offset = offset & 3;
|
|
||||||
details.size = size;
|
|
||||||
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_READ;
|
|
||||||
uint32_t result = this->dev_map[dev_num]->pci_cfg_read(reg_offs, details);
|
|
||||||
return pci_conv_rd_data(result, details);
|
|
||||||
} else {
|
|
||||||
LOG_F(ERROR, "%s: read attempt from non-existing PCI device ??:%02x.%x @%02x",
|
|
||||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPC106::pci_write(uint32_t offset, uint32_t value, uint32_t size) {
|
void MPC106::pci_write(uint32_t offset, uint32_t value, uint32_t size) {
|
||||||
int bus_num = (this->config_addr >> 8) & 0xFF;
|
int bus_num, dev_num, fun_num;
|
||||||
int dev_num = (this->config_addr >> 19) & 0x1F;
|
uint8_t reg_offs;
|
||||||
int fun_num = (this->config_addr >> 16) & 0x07;
|
AccessDetails details;
|
||||||
int reg_offs = (this->config_addr >> 24) & 0xFC;
|
PCIDevice *device;
|
||||||
|
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
|
||||||
if (bus_num) {
|
details.flags |= PCI_CONFIG_WRITE;
|
||||||
LOG_F(ERROR, "%s: write attempt to non-local PCI bus, %02x:%02x.%x @%02x",
|
if (device) {
|
||||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3));
|
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
|
||||||
|
device->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// otherwise perform necessary data transformations -> slow path
|
||||||
|
uint32_t old_val = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
|
||||||
|
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
|
||||||
|
device->pci_cfg_write(reg_offs, new_val, details);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
|
||||||
|
}
|
||||||
|
|
||||||
if (this->dev_map.count(dev_num)) {
|
inline 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)
|
||||||
AccessDetails details;
|
{
|
||||||
details.offset = offset & 3;
|
device = NULL;
|
||||||
details.size = size;
|
details.size = size;
|
||||||
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_WRITE;
|
details.offset = offset & 3;
|
||||||
|
|
||||||
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
|
bus_num = (this->config_addr >> 8) & 0xFF;
|
||||||
this->dev_map[dev_num]->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
|
dev_num = (this->config_addr >> 19) & 0x1F;
|
||||||
} else { // otherwise perform necessary data transformations -> slow path
|
fun_num = (this->config_addr >> 16) & 0x07;
|
||||||
uint32_t old_val = this->dev_map[dev_num]->pci_cfg_read(reg_offs, details);
|
reg_offs = (this->config_addr >> 24) & 0xFC;
|
||||||
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
|
|
||||||
this->dev_map[dev_num]->pci_cfg_write(reg_offs, new_val, details);
|
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 (this->dev_map.count(DEV_FUN(dev_num, fun_num))) {
|
||||||
|
device = this->dev_map[DEV_FUN(dev_num, fun_num)];
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
LOG_F(ERROR, "%s: write attempt to non-existing PCI device ??:%02x.%x @%02x",
|
|
||||||
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,13 +92,11 @@ protected:
|
|||||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||||
|
|
||||||
bool supports_io_space(void) {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
void setup_ram(void);
|
void setup_ram(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
inline void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t ®_offs, AccessDetails &details, PCIDevice *&device);
|
||||||
|
|
||||||
uint32_t config_addr;
|
uint32_t config_addr;
|
||||||
|
|
||||||
uint16_t pmcr1 = 0; // power management config 1
|
uint16_t pmcr1 = 0; // power management config 1
|
||||||
|
@ -348,17 +348,13 @@ void ATIRage::write_reg(uint32_t offset, uint32_t value, uint32_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ATIRage::io_access_allowed(uint32_t offset) {
|
bool ATIRage::io_access_allowed(uint32_t offset) {
|
||||||
if (!(this->command & 1)) {
|
if (offset >= this->io_base && offset < (this->io_base + 0x100)) {
|
||||||
|
if (this->command & 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
LOG_F(WARNING, "ATI I/O space disabled in the command reg");
|
LOG_F(WARNING, "ATI I/O space disabled in the command reg");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
if (offset < this->io_base || offset > (this->io_base + 0x100)) {
|
|
||||||
LOG_F(WARNING, "Rage: I/O out of range, base=0x%X, offset=0x%X", io_base, offset);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,10 +60,6 @@ public:
|
|||||||
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
|
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
|
||||||
|
|
||||||
/* PCI device methods */
|
/* PCI device methods */
|
||||||
bool supports_io_space(void) {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ int initialize_catalyst(std::string& id)
|
|||||||
|
|
||||||
// add the GrandCentral I/O controller
|
// add the GrandCentral I/O controller
|
||||||
pci_host->pci_register_device(
|
pci_host->pci_register_device(
|
||||||
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
|
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
|
||||||
|
|
||||||
// get (raw) pointer to the memory controller
|
// get (raw) pointer to the memory controller
|
||||||
platinum_obj = dynamic_cast<PlatinumCtrl*>(gMachineObj->get_comp_by_name("Platinum"));
|
platinum_obj = dynamic_cast<PlatinumCtrl*>(gMachineObj->get_comp_by_name("Platinum"));
|
||||||
|
@ -53,7 +53,7 @@ int initialize_gazelle(std::string& id)
|
|||||||
|
|
||||||
// register O'Hare I/O controller with the main PCI bus
|
// register O'Hare I/O controller with the main PCI bus
|
||||||
pci_host->pci_register_device(
|
pci_host->pci_register_device(
|
||||||
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("OHare")));
|
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("OHare")));
|
||||||
|
|
||||||
PsxCtrl* psx_obj = dynamic_cast<PsxCtrl*>(gMachineObj->get_comp_by_name("Psx"));
|
PsxCtrl* psx_obj = dynamic_cast<PsxCtrl*>(gMachineObj->get_comp_by_name("Psx"));
|
||||||
|
|
||||||
|
@ -124,9 +124,9 @@ int initialize_gossamer(std::string& id)
|
|||||||
|
|
||||||
// add pci devices
|
// add pci devices
|
||||||
grackle_obj->pci_register_device(
|
grackle_obj->pci_register_device(
|
||||||
16, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
|
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
|
||||||
grackle_obj->pci_register_device(
|
grackle_obj->pci_register_device(
|
||||||
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(id == "pmg3twr" ? "AtiRagePro" : "AtiRageGT")));
|
DEV_FUN(0x12,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(id == "pmg3twr" ? "AtiRagePro" : "AtiRageGT")));
|
||||||
|
|
||||||
// add Athens clock generator device and register it with the I2C host
|
// add Athens clock generator device and register it with the I2C host
|
||||||
gMachineObj->add_device("Athens", std::unique_ptr<AthensClocks>(new AthensClocks(0x28)));
|
gMachineObj->add_device("Athens", std::unique_ptr<AthensClocks>(new AthensClocks(0x28)));
|
||||||
|
@ -44,14 +44,14 @@ int initialize_tnt(std::string& id)
|
|||||||
|
|
||||||
// connect GrandCentral I/O controller to the PCI1 bus
|
// connect GrandCentral I/O controller to the PCI1 bus
|
||||||
pci_host->pci_register_device(
|
pci_host->pci_register_device(
|
||||||
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
|
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
|
||||||
|
|
||||||
// get video PCI controller object
|
// get video PCI controller object
|
||||||
PCIHost *vci_host = dynamic_cast<PCIHost*>(gMachineObj->get_comp_by_name("Chaos"));
|
PCIHost *vci_host = dynamic_cast<PCIHost*>(gMachineObj->get_comp_by_name("Chaos"));
|
||||||
|
|
||||||
// connect built-in video device to the VCI bus
|
// connect built-in video device to the VCI bus
|
||||||
vci_host->pci_register_device(
|
vci_host->pci_register_device(
|
||||||
1, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ControlVideo")));
|
DEV_FUN(0x0B,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ControlVideo")));
|
||||||
|
|
||||||
// get (raw) pointer to the memory controller
|
// get (raw) pointer to the memory controller
|
||||||
memctrl_obj = dynamic_cast<HammerheadCtrl*>(gMachineObj->get_comp_by_name("Hammerhead"));
|
memctrl_obj = dynamic_cast<HammerheadCtrl*>(gMachineObj->get_comp_by_name("Hammerhead"));
|
||||||
|
@ -27,7 +27,7 @@ It also spans for 0x7F000000 bytes starting from 0x80000000.
|
|||||||
|
|
||||||
The dingusppc CLI can add known PCI devices to a PCI slot (A1, B1, C1).
|
The dingusppc CLI can add known PCI devices to a PCI slot (A1, B1, C1).
|
||||||
|
|
||||||
Only the following device numbers are probed by Open Firmware:
|
Only the following device numbers connected to grackle are probed by Open Firmware:
|
||||||
|
|
||||||
- @0 used by pci [grackle]
|
- @0 used by pci [grackle]
|
||||||
- @c slot PERCH interrupt 0x1c
|
- @c slot PERCH interrupt 0x1c
|
||||||
@ -37,6 +37,8 @@ Only the following device numbers are probed by Open Firmware:
|
|||||||
- @10 used by mac-io [heathrow] which includes many devices with different interrupts
|
- @10 used by mac-io [heathrow] which includes many devices with different interrupts
|
||||||
- @12 slot F1 interrupt 0x16 used by AtiRageGT or AtiRagePro
|
- @12 slot F1 interrupt 0x16 used by AtiRageGT or AtiRagePro
|
||||||
|
|
||||||
With minor additions to source code, dingusppc can add a known PCI device to any device number between @1 and @1f except for @10 and @12.
|
With minor additions to source code, dingusppc can add a known PCI device to any device number between @1 and @1f except for @10 and @12. A nvramrc patch can make Open Firmware probe the other device numbers. An OS might be able to probe these other device numbers even if they are not probed by Open Firmware.
|
||||||
|
|
||||||
A nvramrc patch can make Open Firmware probe the other device numbers. An OS might be able to probe these other devices numbers even if they are not probed by Open Firmware.
|
A better approach may be to attach a PCI bridge to one of the supported slots. Then additional devices can be attached to the PCI bridge. For a PCI bridge, Open Firmware will probe device numbers between @0 and @f. Some nvramrc patches could maybe make Open Firmware probe the PCI bridge devices up to device number @1f. PCI bridges can be nested. In any case, each device number can have up to 8 functions numbered from 0 to 7.
|
||||||
|
|
||||||
|
The dingusppc CLI doesn't yet support adding devices to PCI bridges or adding function numbers 1 to 7.
|
||||||
|
Loading…
Reference in New Issue
Block a user