diff --git a/devices/common/pci/bandit.cpp b/devices/common/pci/bandit.cpp
index 0a05c68..e408583 100644
--- a/devices/common/pci/bandit.cpp
+++ b/devices/common/pci/bandit.cpp
@@ -143,7 +143,7 @@ uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
- PCIDevice *device;
+ PCIBase *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_READ;
if (device) {
@@ -167,7 +167,7 @@ void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
- PCIDevice *device;
+ PCIBase *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_WRITE;
if (device) {
@@ -195,7 +195,7 @@ void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
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)
+ AccessDetails &details, PCIBase *&device)
{
device = NULL;
details.size = size;
diff --git a/devices/common/pci/bandit.h b/devices/common/pci/bandit.h
index 2e518e6..60e60f4 100644
--- a/devices/common/pci/bandit.h
+++ b/devices/common/pci/bandit.h
@@ -82,7 +82,7 @@ protected:
private:
void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num,
int &fun_num, uint8_t ®_offs, AccessDetails &details,
- PCIDevice *&device);
+ PCIBase *&device);
};
/*
diff --git a/devices/common/pci/pcibase.cpp b/devices/common/pci/pcibase.cpp
new file mode 100644
index 0000000..08f24b7
--- /dev/null
+++ b/devices/common/pci/pcibase.cpp
@@ -0,0 +1,304 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+PCIBase::PCIBase(std::string name, PCIHeaderType hdr_type, int num_bars)
+{
+ this->name = name;
+ this->pci_name = name;
+ this->hdr_type = hdr_type;
+ this->num_bars = num_bars;
+
+ this->pci_rd_stat = [this]() { return this->status; };
+ this->pci_rd_cmd = [this]() { return this->command; };
+ this->pci_rd_bist = []() { return 0; };
+ this->pci_rd_lat_timer = [this]() { return this->lat_timer; };
+ this->pci_rd_cache_lnsz = [this]() { return this->cache_ln_sz; };
+
+ this->pci_wr_stat = [](uint16_t val) {};
+ this->pci_wr_cmd = [this](uint16_t cmd) {
+ /*
+ FIXME: should register or unregister BAR mmio regions if (cmd & 2) changes.
+ Or the mmio regions should be enabled/disabled.
+ */
+ this->command = cmd;
+ };
+ this->pci_wr_bist = [](uint8_t val) {};
+ this->pci_wr_lat_timer = [this](uint8_t val) { this->lat_timer = val; };
+ this->pci_wr_cache_lnsz = [this](uint8_t val) { this->cache_ln_sz = val; };
+
+ this->pci_notify_bar_change = [](int bar_num) {};
+};
+
+uint32_t PCIBase::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
+{
+ switch (reg_offs) {
+ case PCI_CFG_DEV_ID:
+ return (this->device_id << 16) | (this->vendor_id);
+ case PCI_CFG_STAT_CMD:
+ return (this->pci_rd_stat() << 16) | (this->pci_rd_cmd());
+ case PCI_CFG_CLASS_REV:
+ return this->class_rev;
+ case PCI_CFG_DWORD_3:
+ return (pci_rd_bist() << 24) | (this->hdr_type << 16) |
+ (pci_rd_lat_timer() << 8) | pci_rd_cache_lnsz();
+ }
+ LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
+ return 0;
+}
+
+void PCIBase::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
+{
+ switch (reg_offs) {
+ case PCI_CFG_STAT_CMD:
+ this->pci_wr_stat(value >> 16);
+ this->pci_wr_cmd(value & 0xFFFFU);
+ break;
+ case PCI_CFG_DWORD_3:
+ this->pci_wr_bist(value >> 24);
+ this->pci_wr_lat_timer((value >> 8) & 0xFF);
+ this->pci_wr_cache_lnsz(value & 0xFF);
+ break;
+ default:
+ LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
+ }
+}
+
+void PCIBase::setup_bars(std::vector cfg_data)
+{
+ for (auto cfg_entry : cfg_data) {
+ if (cfg_entry.bar_num > this->num_bars) {
+ ABORT_F("BAR number %d out of range", cfg_entry.bar_num);
+ }
+ this->bars_cfg[cfg_entry.bar_num] = cfg_entry.bar_cfg;
+ }
+
+ this->finish_config_bars();
+}
+
+int PCIBase::attach_exp_rom_image(const std::string img_path)
+{
+ std::ifstream img_file;
+
+ int result = 0;
+
+ this->exp_bar_cfg = 0; // tell the world we got no ROM for now
+
+ try {
+ img_file.open(img_path, std::ios::in | std::ios::binary);
+ if (img_file.fail()) {
+ throw std::runtime_error("could not open specified ROM dump image");
+ }
+
+ // validate image file
+ uint8_t buf[4] = { 0 };
+
+ img_file.seekg(0, std::ios::beg);
+ img_file.read((char *)buf, sizeof(buf));
+
+ if (buf[0] != 0x55 || buf[1] != 0xAA) {
+ throw std::runtime_error("invalid expansion ROM signature");
+ }
+
+ // determine image size
+ img_file.seekg(0, std::ios::end);
+ size_t exp_rom_image_size = img_file.tellg();
+ if (exp_rom_image_size > 4*1024*1024) {
+ throw std::runtime_error("expansion ROM file too large");
+ }
+
+ // verify PCI struct offset
+ uint16_t pci_struct_offset = 0;
+ img_file.seekg(0x18, std::ios::beg);
+ img_file.read((char *)&pci_struct_offset, sizeof(pci_struct_offset));
+
+ if (pci_struct_offset > exp_rom_image_size) {
+ throw std::runtime_error("invalid PCI structure offset");
+ }
+
+ // verify PCI struct signature
+ img_file.seekg(pci_struct_offset, std::ios::beg);
+ img_file.read((char *)buf, sizeof(buf));
+
+ if (buf[0] != 'P' || buf[1] != 'C' || buf[2] != 'I' || buf[3] != 'R') {
+ throw std::runtime_error("unexpected PCI struct signature");
+ }
+
+ // find minimum rom size for the rom file (power of 2 >= 0x800)
+ for (this->exp_rom_size = 1 << 11; this->exp_rom_size < exp_rom_image_size; this->exp_rom_size <<= 1) {}
+
+ // ROM image ok - go ahead and load it
+ this->exp_rom_data = std::unique_ptr (new uint8_t[this->exp_rom_size]);
+ img_file.seekg(0, std::ios::beg);
+ img_file.read((char *)this->exp_rom_data.get(), exp_rom_image_size);
+ memset(&this->exp_rom_data[exp_rom_image_size], 0xff, this->exp_rom_size - exp_rom_image_size);
+
+ if (exp_rom_image_size == this->exp_rom_size) {
+ LOG_F(INFO, "%s: loaded expansion rom (%d bytes).",
+ this->pci_name.c_str(), this->exp_rom_size);
+ }
+ else {
+ LOG_F(WARNING, "%s: loaded expansion rom (%d bytes adjusted to %d bytes).",
+ this->pci_name.c_str(), (int)exp_rom_image_size, this->exp_rom_size);
+ }
+
+ this->exp_bar_cfg = ~(this->exp_rom_size - 1);
+ }
+ catch (const std::exception& exc) {
+ LOG_F(ERROR, "%s: %s", this->pci_name.c_str(), exc.what());
+ result = -1;
+ }
+
+ img_file.close();
+
+ return result;
+}
+
+void PCIBase::set_bar_value(int bar_num, uint32_t value)
+{
+ uint32_t bar_cfg = this->bars_cfg[bar_num];
+ switch (bars_typ[bar_num]) {
+ case PCIBarType::Unused:
+ return;
+
+ case PCIBarType::Io_16_Bit:
+ case PCIBarType::Io_32_Bit:
+ this->bars[bar_num] = (value & bar_cfg & ~3) | (bar_cfg & 3);
+ if (value != 0xFFFFFFFFUL && (value & ~3) != (value & bar_cfg & ~3)) {
+ LOG_F(ERROR, "%s: BAR %d cannot be 0x%08x (set to 0x%08x)", this->pci_name.c_str(), bar_num, (value & ~3), (value & bar_cfg & ~3));
+ }
+ break;
+
+ case PCIBarType::Mem_20_Bit:
+ case PCIBarType::Mem_32_Bit:
+ case PCIBarType::Mem_64_Bit_Lo:
+ this->bars[bar_num] = (value & bar_cfg & ~0xF) | (bar_cfg & 0xF);
+ if (value != 0xFFFFFFFFUL && (value & ~0xF) != (value & bar_cfg & ~0xF)) {
+ LOG_F(ERROR, "%s: BAR %d cannot be 0x%08x (set to 0x%08x)", this->pci_name.c_str(), bar_num, (value & ~0xF), (value & bar_cfg & ~0xF));
+ }
+ break;
+
+ case PCIBarType::Mem_64_Bit_Hi:
+ this->bars[bar_num] = value & bar_cfg;
+ break;
+ }
+
+ if (value != 0xFFFFFFFFUL) // don't notify the device during BAR sizing
+ this->pci_notify_bar_change(bar_num);
+}
+
+void PCIBase::finish_config_bars()
+{
+ for (int bar_num = 0; bar_num < this->num_bars; bar_num++) {
+ uint32_t bar_cfg = this->bars_cfg[bar_num];
+
+ if (!bar_cfg) // skip unimplemented BARs
+ continue;
+
+ if (bar_cfg & 1) {
+ bars_typ[bar_num] = (bar_cfg & 0xffff0000) ? PCIBarType::Io_32_Bit :
+ PCIBarType::Io_16_Bit;
+ has_io_space = true;
+ }
+ else {
+ int pci_space_type = (bar_cfg >> 1) & 3;
+ switch (pci_space_type) {
+ case 0:
+ bars_typ[bar_num] = PCIBarType::Mem_32_Bit;
+ break;
+ case 1:
+ bars_typ[bar_num] = PCIBarType::Mem_20_Bit;
+ break;
+ case 2:
+ if (bar_num >= num_bars - 1) {
+ ABORT_F("%s: BAR %d cannot be 64-bit",
+ this->pci_name.c_str(), bar_num);
+ }
+ else if (this->bars_cfg[bar_num+1] == 0) {
+ ABORT_F("%s: 64-bit BAR %d has zero for upper 32 bits",
+ this->pci_name.c_str(), bar_num);
+ }
+ else {
+ bars_typ[bar_num++] = PCIBarType::Mem_64_Bit_Lo;
+ bars_typ[bar_num ] = PCIBarType::Mem_64_Bit_Hi;
+ }
+ break;
+ default:
+ ABORT_F("%s: invalid or unsupported PCI space type %d for BAR %d",
+ this->pci_name.c_str(), pci_space_type, bar_num);
+ } // switch pci_space_type
+ }
+ } // for bar_num
+}
+
+void PCIBase::map_exp_rom_mem()
+{
+ uint32_t rom_addr = this->exp_rom_bar & this->exp_bar_cfg;
+ if (rom_addr) {
+ if (this->exp_rom_addr != rom_addr) {
+ this->unmap_exp_rom_mem();
+ uint32_t rom_size = ~this->exp_bar_cfg + 1;
+ this->host_instance->pci_register_mmio_region(rom_addr, rom_size, this);
+ this->exp_rom_addr = rom_addr;
+ }
+}
+ else {
+ this->unmap_exp_rom_mem();
+ }
+}
+
+void PCIBase::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 PCIBase::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();
+ }
+ }
+}
diff --git a/devices/common/pci/pcibase.h b/devices/common/pci/pcibase.h
new file mode 100644
index 0000000..fa19ae7
--- /dev/null
+++ b/devices/common/pci/pcibase.h
@@ -0,0 +1,245 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#ifndef PCI_BASE_H
+#define PCI_BASE_H
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+/** PCI configuration space header types */
+enum PCIHeaderType : uint8_t {
+ PCI_HEADER_TYPE_0 = 0, // PCI Device
+ PCI_HEADER_TYPE_1 = 1, // PCI-PCI Bridge
+ PCI_HEADER_TYPE_2 = 2, // PCI-To-Cardbus Bridge
+};
+
+/** PCI configuration space registers offsets */
+enum {
+ PCI_CFG_DEV_ID = 0x00, // device and vendor IDs
+ PCI_CFG_STAT_CMD = 0x04, // command/status register
+ PCI_CFG_CLASS_REV = 0x08, // class code and revision ID
+ PCI_CFG_DWORD_3 = 0x0C, // BIST, HeaderType, Lat_Timer and Cache_Line_Size
+ PCI_CFG_BAR0 = 0x10, // base address register 0 (type 0, 1, and 2)
+ PCI_CFG_BAR1 = 0x14, // base address register 1 (type 0 and 1)
+ PCI_CFG_DWORD_13 = 0x34, // capabilities pointer (type 0 and 1)
+ PCI_CFG_DWORD_15 = 0x3C, // Max_Lat, Min_Gnt (Type 0); Bridge Control (Type 1 and 2); Int_Pin and Int_Line registers (type 0, 1, and 2)
+};
+
+/** PCI Vendor IDs for devices used in Power Macintosh computers. */
+enum {
+ PCI_VENDOR_ATI = 0x1002,
+ PCI_VENDOR_DEC = 0x1011,
+ PCI_VENDOR_MOTOROLA = 0x1057,
+ PCI_VENDOR_APPLE = 0x106B,
+ PCI_VENDOR_NVIDIA = 0x10DE,
+};
+
+/** PCI BAR types */
+enum PCIBarType : uint32_t {
+ Unused = 0,
+ Io_16_Bit,
+ Io_32_Bit,
+ Mem_20_Bit, // legacy type for < 1MB memory
+ Mem_32_Bit,
+ Mem_64_Bit_Lo,
+ Mem_64_Bit_Hi
+};
+
+typedef struct {
+ uint32_t bar_num;
+ uint32_t bar_cfg;
+} BarConfig;
+
+class PCIBase : public MMIODevice {
+public:
+ PCIBase(std::string name, PCIHeaderType hdr_type, int num_bars);
+ virtual ~PCIBase() = default;
+
+ virtual bool supports_io_space() {
+ return has_io_space;
+ };
+
+ /* I/O space access methods */
+ virtual bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res) {
+ return false;
+ };
+
+ virtual bool pci_io_write(uint32_t offset, uint32_t value, uint32_t size) {
+ return false;
+ };
+
+ // configuration space access 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);
+
+ // plugin interface for using in the derived classes
+ std::function pci_rd_stat;
+ std::function pci_wr_stat;
+ std::function pci_rd_cmd;
+ std::function pci_wr_cmd;
+ std::function pci_rd_bist;
+ std::function pci_wr_bist;
+ std::function pci_rd_lat_timer;
+ std::function pci_wr_lat_timer;
+ std::function pci_rd_cache_lnsz;
+ std::function pci_wr_cache_lnsz;
+
+ std::function pci_notify_bar_change;
+
+ int attach_exp_rom_image(const std::string img_path);
+
+ virtual void set_host(PCIHost* 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
+ 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 set_bar_value(int bar_num, uint32_t value);
+ void setup_bars(std::vector cfg_data);
+ void finish_config_bars();
+ void pci_wr_exp_rom_bar(uint32_t data);
+ void map_exp_rom_mem();
+ void unmap_exp_rom_mem();
+
+ std::string pci_name; // human-readable device name
+ PCIHost* host_instance; // host bridge instance to call back
+
+ // PCI configuration space state (type 0, 1, and 2)
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t command = 0;
+ uint16_t status = 0;
+ uint32_t class_rev; // class code and revision id
+ uint8_t cache_ln_sz = 0; // cache line size
+ uint8_t lat_timer = 0; // latency timer
+ uint8_t hdr_type; // header type, single function
+ uint8_t cap_ptr = 0; // capabilities ptr
+
+ uint8_t irq_pin = 0;
+ uint8_t irq_line = 0;
+
+ bool has_io_space = false;
+ int num_bars; // number of BARs. Type 0:6, type 1:2, type 2:1 (4K)
+ uint32_t bars[6] = { 0 }; // BARs (base address registers)
+ uint32_t bars_cfg[6] = { 0 }; // configuration values for BARs
+ PCIBarType bars_typ[6] = { PCIBarType::Unused }; // types for BARs
+
+ // PCI configuration space state (type 0 and 1)
+ uint32_t exp_bar_cfg = 0; // expansion ROM configuration
+ uint32_t exp_rom_bar = 0; // expansion ROM base address register
+ uint32_t exp_rom_addr = 0; // expansion ROM base address
+ uint32_t exp_rom_size = 0; // expansion ROM size in bytes
+
+ std::unique_ptr exp_rom_data;
+};
+
+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_BASE_H */
diff --git a/devices/common/pci/pcibridge.cpp b/devices/common/pci/pcibridge.cpp
index e05a5a1..6ea9971 100644
--- a/devices/common/pci/pcibridge.cpp
+++ b/devices/common/pci/pcibridge.cpp
@@ -24,16 +24,8 @@ along with this program. If not, see .
#include
#include
-PCIBridge::PCIBridge(std::string name) : PCIDevice(name)
+PCIBridge::PCIBridge(std::string name) : PCIBridgeBase(name, PCI_HEADER_TYPE_1, 2)
{
- 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; };
@@ -44,18 +36,6 @@ PCIBridge::PCIBridge(std::string name) : PCIDevice(name)
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);
@@ -114,33 +94,14 @@ PCIBridge::PCIBridge(std::string name) : PCIDevice(name)
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_BAR0:
+ case PCI_CFG_BAR1:
+ return this->bars[(reg_offs - 0x10) >> 2];
case PCI_CFG_IO_BASE:
return (this->pci_rd_sec_status() << 16) |
(this->pci_rd_io_limit() << 8) | (this->pci_rd_io_base());
@@ -154,29 +115,21 @@ uint32_t PCIBridge::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
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:
+ case PCI_CFG_DWORD_13:
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;
+ default:
+ return PCIBridgeBase::pci_cfg_read(reg_offs, details);
}
- 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);
+ case PCI_CFG_BAR0:
+ case PCI_CFG_BAR1:
+ this->set_bar_value((reg_offs - 0x10) >> 2, value);
break;
case PCI_CFG_IO_BASE:
this->pci_wr_sec_status(value >> 16);
@@ -204,12 +157,9 @@ void PCIBridge::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &
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();
+ PCIBridgeBase::pci_cfg_write(reg_offs, value, details);
+ break;
}
}
diff --git a/devices/common/pci/pcibridge.h b/devices/common/pci/pcibridge.h
index 8beba48..3500345 100644
--- a/devices/common/pci/pcibridge.h
+++ b/devices/common/pci/pcibridge.h
@@ -22,7 +22,7 @@ along with this program. If not, see .
#ifndef PCI_BRIDGE_H
#define PCI_BRIDGE_H
-#include
+#include
#include
#include
@@ -30,52 +30,28 @@ along with this program. If not, see .
/** 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;
+class PCIBridge : public PCIBridgeBase {
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
+ // PCIBase 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 pci_rd_primary_bus;
- std::function pci_wr_primary_bus;
- std::function pci_rd_secondary_bus;
- std::function pci_wr_secondary_bus;
- std::function pci_rd_subordinate_bus;
- std::function pci_wr_subordinate_bus;
- std::function pci_rd_sec_latency_timer;
- std::function pci_wr_sec_latency_timer;
- std::function pci_rd_sec_status;
- std::function pci_wr_sec_status;
std::function pci_rd_io_base;
std::function pci_wr_io_base;
std::function pci_rd_io_limit;
@@ -96,16 +72,9 @@ public:
std::function pci_wr_io_base_upper16;
std::function pci_rd_io_limit_upper16;
std::function pci_wr_io_limit_upper16;
- std::function pci_rd_bridge_control;
- std::function 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;
@@ -116,12 +85,8 @@ protected:
uint32_t pref_limit_upper32 = 0;
uint16_t io_base_upper16 = 0;
uint16_t io_limit_upper16 = 0;
- uint16_t bridge_control = 0;
- // 0 = not writable, 0xf8 = limits the granularity to eight PCI clocks
- uint8_t sec_latency_timer_cfg = 0;
-
- // 0 = not writable, 0xf0 = supports 16 bit io range, 0xf1 = supports 32 bit I/O range
+ // 0 = not writable, 0xf0 = supports 16 bit I/O range, 0xf1 = supports 32 bit I/O range
uint8_t io_cfg = 0xf0;
// 0 = not writable, 0xfff0 = supports 32 bit memory range
@@ -131,14 +96,13 @@ protected:
// 0xfff1 = supports 64 bit prefetchable memory range
uint16_t pref_mem_cfg = 0xfff0;
+ // calculated address ranges
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 */
diff --git a/devices/common/pci/pcibridgebase.cpp b/devices/common/pci/pcibridgebase.cpp
new file mode 100644
index 0000000..d40f5bd
--- /dev/null
+++ b/devices/common/pci/pcibridgebase.cpp
@@ -0,0 +1,87 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#include
+#include
+
+PCIBridgeBase::PCIBridgeBase(std::string name, PCIHeaderType hdr_type, int num_bars) : PCIBase(name, hdr_type, num_bars)
+{
+ 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_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_bridge_control = [this](uint16_t val) { this->bridge_control = val; };
+};
+
+bool PCIBridgeBase::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj)
+{
+ // FIXME: constrain region to memory range
+ return this->host_instance->pci_register_mmio_region(start_addr, size, obj);
+}
+
+bool PCIBridgeBase::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj)
+{
+ return this->host_instance->pci_unregister_mmio_region(start_addr, size, obj);
+}
+
+uint32_t PCIBridgeBase::pci_cfg_read(uint32_t reg_offs, AccessDetails &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_DWORD_15:
+ return (this->pci_rd_bridge_control() << 16) | (irq_pin << 8) | irq_line;
+ default:
+ return PCIBase::pci_cfg_read(reg_offs, details);
+ }
+}
+
+void PCIBridgeBase::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &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_DWORD_15:
+ this->irq_line = value >> 24;
+ this->pci_wr_bridge_control(value >> 16);
+ break;
+ default:
+ return PCIBase::pci_cfg_write(reg_offs, value, details);
+ }
+}
diff --git a/devices/common/pci/pcibridgebase.h b/devices/common/pci/pcibridgebase.h
new file mode 100644
index 0000000..2cbbf97
--- /dev/null
+++ b/devices/common/pci/pcibridgebase.h
@@ -0,0 +1,80 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#ifndef PCI_BRIDGE_BASE_H
+#define PCI_BRIDGE_BASE_H
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/** PCI configuration space registers offsets (type 1 and 2) */
+enum {
+ PCI_CFG_PRIMARY_BUS = 0x18, // PRIMARY_BUS, SECONDARY_BUS, SUBORDINATE_BUS, SEC_LATENCY_TIMER
+};
+
+class PCIBridgeBase : public PCIHost, public PCIBase {
+ friend class PCIHost;
+public:
+ PCIBridgeBase(std::string name, PCIHeaderType hdr_type, int num_bars);
+ ~PCIBridgeBase() = default;
+
+ // PCIHost methods
+ virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj);
+ virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj);
+
+ // PCIBase 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);
+
+ // plugin interface for using in the derived classes
+ std::function pci_rd_primary_bus;
+ std::function pci_wr_primary_bus;
+ std::function pci_rd_secondary_bus;
+ std::function pci_wr_secondary_bus;
+ std::function pci_rd_subordinate_bus;
+ std::function pci_wr_subordinate_bus;
+ std::function pci_rd_sec_latency_timer;
+ std::function pci_wr_sec_latency_timer;
+ std::function pci_rd_sec_status;
+ std::function pci_wr_sec_status;
+ std::function pci_rd_bridge_control;
+ std::function 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;
+ uint16_t bridge_control = 0;
+
+ // 0 = not writable, 0xf8 = limits the granularity to eight PCI clocks
+ uint8_t sec_latency_timer_cfg = 0xff;
+};
+
+#endif /* PCI_BRIDGE_BASE_H */
diff --git a/devices/common/pci/pcicardbusbridge.cpp b/devices/common/pci/pcicardbusbridge.cpp
new file mode 100644
index 0000000..039bd7c
--- /dev/null
+++ b/devices/common/pci/pcicardbusbridge.cpp
@@ -0,0 +1,223 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#include
+#include
+
+typedef struct {
+ // CardBus Registers
+ /* 0x00 */ uint32_t Event;
+ /* 0x04 */ uint32_t Mask;
+ /* 0x08 */ uint32_t PresentState;
+ /* 0x0C */ uint32_t Force;
+ /* 0x10 */ uint32_t Control;
+ /* 0x14 */ uint32_t Reserved[4];
+ /* 0x20 */ uint32_t UserDefined20h[504];
+
+ // 16-Bit Registers
+ /* 0x800 */ uint8_t IDAndRevision;
+ /* 0x801 */ uint8_t IFStatus;
+ /* 0x802 */ uint8_t PowerAndResetDrvControl;
+ /* 0x803 */ uint8_t InterruptAndGeneralControl;
+ /* 0x804 */ uint8_t CardStatusChange;
+ /* 0x805 */ uint8_t CardStatusChangeInterruptConfiguration;
+ /* 0x806 */ uint8_t AddressWindowEnable;
+ /* 0x807 */ uint8_t IOControl;
+ /* 0x808 */ uint16_t IOAdd0Start;
+ /* 0x80A */ uint16_t IOAdd0Stop;
+ /* 0x80C */ uint16_t IOAdd1Start;
+ /* 0x80E */ uint16_t IOAdd1Stop;
+ /* 0x810 */ uint16_t SysMemAdd0Start;
+ /* 0x812 */ uint16_t SysMemAdd0Stop;
+ /* 0x814 */ uint16_t CardMemoryOffsetAdd0;
+ /* 0x816 */ uint16_t UserDefined814h;
+ /* 0x818 */ uint16_t SysMemAdd1Start;
+ /* 0x81A */ uint16_t SysMemAdd1Stop;
+ /* 0x81C */ uint16_t CardMemoryOffsetAdd1;
+ /* 0x81E */ uint16_t UserDefined81Ch;
+ /* 0x820 */ uint16_t SysMemAdd2Start;
+ /* 0x822 */ uint16_t SysMemAdd2Stop;
+ /* 0x824 */ uint16_t CardMemoryOffsetAdd2;
+ /* 0x826 */ uint16_t UserDefined824h;
+ /* 0x828 */ uint16_t SysMemAdd3Start;
+ /* 0x82A */ uint16_t SysMemAdd3Stop;
+ /* 0x82C */ uint16_t CardMemoryOffsetAdd3;
+ /* 0x82E */ uint16_t UserDefined82Ch;
+ /* 0x830 */ uint16_t SysMemAdd4Start;
+ /* 0x832 */ uint16_t SysMemAdd4Stop;
+ /* 0x834 */ uint16_t CardMemoryOffsetAdd4;
+ /* 0x836 */ uint16_t UserDefined834h;
+ /* 0x838 */ uint32_t UserDefined838h[4];
+ /* 0x840 */ uint8_t SysMemAdd0MappingStartUp;
+ /* 0x841 */ uint8_t SysMemAdd1MappingStartUp;
+ /* 0x842 */ uint8_t SysMemAdd2MappingStartUp;
+ /* 0x843 */ uint8_t SysMemAdd3MappingStartUp;
+ /* 0x844 */ uint8_t SysMemAdd4MappingStartUp;
+ /* 0x845 */ uint8_t UserDefined845h;
+ /* 0x846 */ uint16_t UserDefined846h;
+ /* 0x848 */ uint32_t UserDefined848h[494];
+} CardBusStatusAnfControlRegisters;
+
+PCICardbusBridge::PCICardbusBridge(std::string name) : PCIBridgeBase(name, PCI_HEADER_TYPE_2, 1)
+{
+ this->pci_rd_memory_base_0 = [this]() { return this->memory_base_0 ; };
+ this->pci_rd_memory_limit_0 = [this]() { return this->memory_limit_0 ; };
+ this->pci_rd_memory_base_1 = [this]() { return this->memory_base_1 ; };
+ this->pci_rd_memory_limit_1 = [this]() { return this->memory_limit_1 ; };
+ this->pci_rd_io_base_0 = [this]() { return this->io_base_0 ; };
+ this->pci_rd_io_limit_0 = [this]() { return this->io_limit_0 ; };
+ this->pci_rd_io_base_1 = [this]() { return this->io_base_1 ; };
+ this->pci_rd_io_limit_1 = [this]() { return this->io_limit_1 ; };
+
+ this->pci_wr_memory_base_0 = [this](uint32_t val) {
+ this->memory_base_0 = val & this->memory_0_cfg;
+ this->memory_base_0_32 = this->memory_base_0;
+ };
+
+ this->pci_wr_memory_limit_0 = [this](uint32_t val) {
+ this->memory_limit_0 = val & this->memory_0_cfg;
+ this->memory_limit_0_32 = this->memory_limit_0 + 0x1000;
+ };
+
+ this->pci_wr_memory_base_1 = [this](uint32_t val) {
+ this->memory_base_1 = val & this->memory_1_cfg;
+ this->memory_base_1_32 = this->memory_base_1;
+ };
+
+ this->pci_wr_memory_limit_1 = [this](uint32_t val) {
+ this->memory_limit_1 = val & this->memory_1_cfg;
+ this->memory_limit_1_32 = this->memory_limit_1 + 0x1000;
+ };
+
+ this->pci_wr_io_base_0 = [this](uint32_t val) {
+ this->io_base_0 = (val & this->io_0_cfg) | (io_0_cfg & 3);
+ this->io_base_0_32 = (this->io_base_0 & ~3);
+ };
+
+ this->pci_wr_io_limit_0 = [this](uint32_t val) {
+ this->io_limit_0 = (val & this->io_0_cfg);
+ this->io_limit_0_32 = this->io_limit_0 + 4;
+ };
+
+ this->pci_wr_io_base_1 = [this](uint32_t val) {
+ this->io_base_1 = (val & this->io_1_cfg) | (io_1_cfg & 3);
+ this->io_base_1_32 = (this->io_base_1 & ~3);
+ };
+
+ this->pci_wr_io_limit_1 = [this](uint32_t val) {
+ this->io_limit_1 = (val & this->io_1_cfg);
+ this->io_limit_1_32 = this->io_limit_1 + 4;
+ };
+};
+
+uint32_t PCICardbusBridge::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
+{
+ switch (reg_offs) {
+ case PCI_CFG_BAR0:
+ return this->bars[(reg_offs - 0x10) >> 2];
+ case PCI_CFG_CB_CAPABILITIES:
+ return (this->pci_rd_sec_status() << 16) | cap_ptr;
+ case PCI_CFG_CB_MEMORY_BASE_0:
+ return this->pci_rd_memory_base_0();
+ case PCI_CFG_CB_MEMORY_LIMIT_0:
+ return this->pci_rd_memory_limit_0();
+ case PCI_CFG_CB_MEMORY_BASE_1:
+ return this->pci_rd_memory_base_0();
+ case PCI_CFG_CB_MEMORY_LIMIT_1:
+ return this->pci_rd_memory_limit_0();
+ case PCI_CFG_CB_IO_BASE_0:
+ return this->pci_rd_io_base_0();
+ case PCI_CFG_CB_IO_LIMIT_0:
+ return this->pci_rd_io_limit_0();
+ case PCI_CFG_CB_IO_BASE_1:
+ return this->pci_rd_io_base_0();
+ case PCI_CFG_CB_IO_LIMIT_1:
+ return this->pci_rd_io_limit_0();
+ case PCI_CFG_CB_SUBSYSTEM_IDS:
+ return (this->subsys_id << 16) | (this->subsys_vndr);
+ case PCI_CFG_CB_LEGACY_MODE_BASE:
+ return this->legacy_mode_base;
+ default:
+ return PCIBridgeBase::pci_cfg_read(reg_offs, details);
+ }
+}
+
+void PCICardbusBridge::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
+{
+ switch (reg_offs) {
+ case PCI_CFG_BAR0:
+ this->set_bar_value((reg_offs - 0x10) >> 2, value);
+ break;
+ case PCI_CFG_CB_CAPABILITIES:
+ this->pci_wr_sec_status(value >> 16);
+ break;
+ case PCI_CFG_CB_MEMORY_BASE_0:
+ this->pci_wr_memory_base_0(value);
+ break;
+ case PCI_CFG_CB_MEMORY_LIMIT_0:
+ this->pci_wr_memory_limit_0(value);
+ break;
+ case PCI_CFG_CB_MEMORY_BASE_1:
+ this->pci_wr_memory_base_0(value);
+ break;
+ case PCI_CFG_CB_MEMORY_LIMIT_1:
+ this->pci_wr_memory_limit_0(value);
+ break;
+ case PCI_CFG_CB_IO_BASE_0:
+ this->pci_wr_io_base_0(value);
+ break;
+ case PCI_CFG_CB_IO_LIMIT_0:
+ this->pci_wr_io_limit_0(value);
+ break;
+ case PCI_CFG_CB_IO_BASE_1:
+ this->pci_wr_io_base_0(value);
+ break;
+ case PCI_CFG_CB_IO_LIMIT_1:
+ this->pci_wr_io_limit_0(value);
+ break;
+/*
+ case PCI_CFG_CB_LEGACY_MODE_BASE:
+ this->legacy_mode_base = value;
+ break;
+*/
+ default:
+ PCIBridgeBase::pci_cfg_write(reg_offs, value, details);
+ break;
+ }
+}
+
+bool PCICardbusBridge::pci_io_read(uint32_t offset, uint32_t size, uint32_t* res)
+{
+ if (!(this->command & 1)) return false;
+ if ((offset < this->io_base_0_32 || offset + size >= this->io_limit_0_32) &&
+ (offset < this->io_base_1_32 || offset + size >= this->io_limit_1_32)
+ ) return false;
+ return this->pci_io_read_loop(offset, size, *res);
+}
+
+bool PCICardbusBridge::pci_io_write(uint32_t offset, uint32_t value, uint32_t size)
+{
+ if (!(this->command & 1)) return false;
+ if ((offset < this->io_base_0_32 || offset + size >= this->io_limit_0_32) &&
+ (offset < this->io_base_1_32 || offset + size >= this->io_limit_1_32)
+ ) return false;
+ return this->pci_io_read_loop(offset, size, value);
+}
diff --git a/devices/common/pci/pcicardbusbridge.h b/devices/common/pci/pcicardbusbridge.h
new file mode 100644
index 0000000..dfa390d
--- /dev/null
+++ b/devices/common/pci/pcicardbusbridge.h
@@ -0,0 +1,112 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-23 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 .
+*/
+
+#ifndef PCI_CARDBUSBRIDGE_H
+#define PCI_CARDBUSBRIDGE_H
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/** PCI configuration space registers offsets */
+enum {
+ PCI_CFG_CB_CAPABILITIES = 0x14, // CB_CAPABILITIES.b, 0.b, CB_SEC_STATUS.w
+ PCI_CFG_CB_MEMORY_BASE_0 = 0x1C,
+ PCI_CFG_CB_MEMORY_LIMIT_0 = 0x20,
+ PCI_CFG_CB_MEMORY_BASE_1 = 0x24,
+ PCI_CFG_CB_MEMORY_LIMIT_1 = 0x28,
+ PCI_CFG_CB_IO_BASE_0 = 0x2C,
+ PCI_CFG_CB_IO_LIMIT_0 = 0x30,
+ PCI_CFG_CB_IO_BASE_1 = 0x34,
+ PCI_CFG_CB_IO_LIMIT_1 = 0x38,
+ PCI_CFG_CB_SUBSYSTEM_IDS = 0x40, // CB_SUBSYSTEM_VENDOR_ID.w, CB_SUBSYSTEM_ID.w
+ PCI_CFG_CB_LEGACY_MODE_BASE = 0x44,
+};
+
+class PCICardbusBridge : public PCIBridgeBase {
+public:
+ PCICardbusBridge(std::string name);
+ ~PCICardbusBridge() = default;
+
+ // PCIBase 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);
+
+ // plugin interface for using in the derived classes
+ std::function pci_rd_memory_base_0;
+ std::function pci_wr_memory_base_0;
+ std::function pci_rd_memory_limit_0;
+ std::function pci_wr_memory_limit_0;
+ std::function pci_rd_memory_base_1;
+ std::function pci_wr_memory_base_1;
+ std::function pci_rd_memory_limit_1;
+ std::function pci_wr_memory_limit_1;
+ std::function pci_rd_io_base_0;
+ std::function pci_wr_io_base_0;
+ std::function pci_rd_io_limit_0;
+ std::function pci_wr_io_limit_0;
+ std::function pci_rd_io_base_1;
+ std::function pci_wr_io_base_1;
+ std::function pci_rd_io_limit_1;
+ std::function pci_wr_io_limit_1;
+
+protected:
+ // PCI configuration space state
+ uint32_t memory_base_0 = 0;
+ uint32_t memory_limit_0 = 0;
+ uint32_t memory_base_1 = 0;
+ uint32_t memory_limit_1 = 0;
+ uint32_t io_base_0 = 0;
+ uint32_t io_limit_0 = 0;
+ uint32_t io_base_1 = 0;
+ uint32_t io_limit_1 = 0;
+ uint16_t subsys_id = 0;
+ uint16_t subsys_vndr = 0;
+ uint32_t legacy_mode_base = 0;
+
+ // 0 = not writable
+ uint32_t memory_0_cfg = 0xfffff000;
+ uint32_t memory_1_cfg = 0xfffff000;
+
+ // 0 = not writable, 0x0000fffc = supports 16 bit I/O range, 0xfffffffd = supports 32 bit I/O range
+ uint32_t io_0_cfg = 0x0000fffc;
+ uint32_t io_1_cfg = 0x0000fffc;
+
+ // calculated address ranges
+ uint32_t memory_base_0_32 = 0;
+ uint32_t memory_limit_0_32 = 0;
+ uint32_t memory_base_1_32 = 0;
+ uint32_t memory_limit_1_32 = 0;
+ uint32_t io_base_0_32 = 0;
+ uint32_t io_limit_0_32 = 0;
+ uint32_t io_base_1_32 = 0;
+ uint32_t io_limit_1_32 = 0;
+};
+
+#endif /* PCI_CARDBUSBRIDGE_H */
diff --git a/devices/common/pci/pcidevice.cpp b/devices/common/pci/pcidevice.cpp
index 4530fad..75eac5b 100644
--- a/devices/common/pci/pcidevice.cpp
+++ b/devices/common/pci/pcidevice.cpp
@@ -27,38 +27,13 @@ along with this program. If not, see .
#include
#include
-PCIDevice::PCIDevice(std::string name)
+PCIDevice::PCIDevice(std::string name) : PCIBase(name, PCI_HEADER_TYPE_0, 6)
{
- this->name = name;
- this->pci_name = name;
-
- this->pci_rd_stat = [this]() { return this->status; };
- this->pci_rd_cmd = [this]() { return this->command; };
- this->pci_rd_bist = []() { return 0; };
- this->pci_rd_lat_timer = [this]() { return this->lat_timer; };
- this->pci_rd_cache_lnsz = [this]() { return this->cache_ln_sz; };
-
- this->pci_wr_stat = [](uint16_t val) {};
- this->pci_wr_cmd = [this](uint16_t cmd) { this->command = cmd; };
- this->pci_wr_bist = [](uint8_t val) {};
- this->pci_wr_lat_timer = [this](uint8_t val) { this->lat_timer = val; };
- this->pci_wr_cache_lnsz = [this](uint8_t val) { this->cache_ln_sz = val; };
-
- this->pci_notify_bar_change = [](int bar_num) {};
};
uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
{
switch (reg_offs) {
- case PCI_CFG_DEV_ID:
- return (this->device_id << 16) | (this->vendor_id);
- case PCI_CFG_STAT_CMD:
- return (this->pci_rd_stat() << 16) | (this->pci_rd_cmd());
- case PCI_CFG_CLASS_REV:
- return this->class_rev;
- case PCI_CFG_DWORD_3:
- 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:
@@ -70,27 +45,18 @@ uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
return (this->subsys_id << 16) | (this->subsys_vndr);
case PCI_CFG_ROM_BAR:
return this->exp_rom_bar;
+ case PCI_CFG_DWORD_13:
+ return cap_ptr;
case PCI_CFG_DWORD_15:
return (max_lat << 24) | (min_gnt << 16) | (irq_pin << 8) | irq_line;
- case PCI_CFG_CAP_PTR:
- return cap_ptr;
+ default:
+ return PCIBase::pci_cfg_read(reg_offs, details);
}
- LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
- return 0;
}
void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
{
switch (reg_offs) {
- case PCI_CFG_STAT_CMD:
- this->pci_wr_stat(value >> 16);
- this->pci_wr_cmd(value & 0xFFFFU);
- break;
- case PCI_CFG_DWORD_3:
- 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:
case PCI_CFG_BAR2:
@@ -106,218 +72,6 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &
this->irq_line = value >> 24;
break;
default:
- LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
- }
-}
-
-void PCIDevice::setup_bars(std::vector cfg_data)
-{
- for (auto cfg_entry : cfg_data) {
- if (cfg_entry.bar_num > 5) {
- ABORT_F("BAR number %d out of range", cfg_entry.bar_num);
- }
- this->bars_cfg[cfg_entry.bar_num] = cfg_entry.bar_cfg;
- }
-
- this->finish_config_bars();
-}
-
-int PCIDevice::attach_exp_rom_image(const std::string img_path)
-{
- std::ifstream img_file;
-
- int result = 0;
-
- this->exp_bar_cfg = 0; // tell the world we got no ROM for now
-
- try {
- img_file.open(img_path, std::ios::in | std::ios::binary);
- if (img_file.fail()) {
- throw std::runtime_error("could not open specified ROM dump image");
- }
-
- // validate image file
- uint8_t buf[4] = { 0 };
-
- img_file.seekg(0, std::ios::beg);
- img_file.read((char *)buf, sizeof(buf));
-
- if (buf[0] != 0x55 || buf[1] != 0xAA) {
- throw std::runtime_error("invalid expansion ROM signature");
- }
-
- // determine image size
- img_file.seekg(0, std::ios::end);
- size_t exp_rom_image_size = img_file.tellg();
- if (exp_rom_image_size > 4*1024*1024) {
- throw std::runtime_error("expansion ROM file too large");
- }
-
- // verify PCI struct offset
- uint16_t pci_struct_offset = 0;
- img_file.seekg(0x18, std::ios::beg);
- img_file.read((char *)&pci_struct_offset, sizeof(pci_struct_offset));
-
- if (pci_struct_offset > exp_rom_image_size) {
- throw std::runtime_error("invalid PCI structure offset");
- }
-
- // verify PCI struct signature
- img_file.seekg(pci_struct_offset, std::ios::beg);
- img_file.read((char *)buf, sizeof(buf));
-
- if (buf[0] != 'P' || buf[1] != 'C' || buf[2] != 'I' || buf[3] != 'R') {
- throw std::runtime_error("unexpected PCI struct signature");
- }
-
- // find minimum rom size for the rom file (power of 2 >= 0x800)
- for (this->exp_rom_size = 1 << 11; this->exp_rom_size < exp_rom_image_size; this->exp_rom_size <<= 1) {}
-
- // ROM image ok - go ahead and load it
- this->exp_rom_data = std::unique_ptr (new uint8_t[this->exp_rom_size]);
- img_file.seekg(0, std::ios::beg);
- img_file.read((char *)this->exp_rom_data.get(), exp_rom_image_size);
- memset(&this->exp_rom_data[exp_rom_image_size], 0xff, this->exp_rom_size - exp_rom_image_size);
-
- if (exp_rom_image_size == this->exp_rom_size) {
- LOG_F(INFO, "%s: loaded expansion rom (%d bytes).",
- this->pci_name.c_str(), this->exp_rom_size);
- }
- else {
- LOG_F(WARNING, "%s: loaded expansion rom (%d bytes adjusted to %d bytes).",
- this->pci_name.c_str(), (int)exp_rom_image_size, this->exp_rom_size);
- }
-
- this->exp_bar_cfg = ~(this->exp_rom_size - 1);
- }
- catch (const std::exception& exc) {
- LOG_F(ERROR, "%s: %s", this->pci_name.c_str(), exc.what());
- result = -1;
- }
-
- img_file.close();
-
- return result;
-}
-
-void PCIDevice::set_bar_value(int bar_num, uint32_t value)
-{
- uint32_t bar_cfg = this->bars_cfg[bar_num];
- switch (bars_typ[bar_num]) {
- case PCIBarType::Unused:
- return;
-
- case PCIBarType::Io_16_Bit:
- case PCIBarType::Io_32_Bit:
- this->bars[bar_num] = (value & bar_cfg & ~3) | (bar_cfg & 3);
- if (value != 0xFFFFFFFFUL && (value & ~3) != (value & bar_cfg & ~3)) {
- LOG_F(ERROR, "%s: BAR %d cannot be 0x%08x (set to 0x%08x)", this->pci_name.c_str(), bar_num, (value & ~3), (value & bar_cfg & ~3));
- }
- break;
-
- case PCIBarType::Mem_20_Bit:
- case PCIBarType::Mem_32_Bit:
- case PCIBarType::Mem_64_Bit_Lo:
- this->bars[bar_num] = (value & bar_cfg & ~0xF) | (bar_cfg & 0xF);
- if (value != 0xFFFFFFFFUL && (value & ~0xF) != (value & bar_cfg & ~0xF)) {
- LOG_F(ERROR, "%s: BAR %d cannot be 0x%08x (set to 0x%08x)", this->pci_name.c_str(), bar_num, (value & ~0xF), (value & bar_cfg & ~0xF));
- }
- break;
-
- case PCIBarType::Mem_64_Bit_Hi:
- this->bars[bar_num] = value & bar_cfg;
- break;
- }
-
- if (value != 0xFFFFFFFFUL) // don't notify the device during BAR sizing
- this->pci_notify_bar_change(bar_num);
-}
-
-void PCIDevice::finish_config_bars()
-{
- for (int bar_num = 0; bar_num < this->num_bars; bar_num++) {
- uint32_t bar_cfg = this->bars_cfg[bar_num];
-
- if (!bar_cfg) // skip unimplemented BARs
- continue;
-
- if (bar_cfg & 1) {
- bars_typ[bar_num] = (bar_cfg & 0xffff0000) ? PCIBarType::Io_32_Bit :
- PCIBarType::Io_16_Bit;
- has_io_space = true;
- }
- else {
- int pci_space_type = (bar_cfg >> 1) & 3;
- switch (pci_space_type) {
- case 0:
- bars_typ[bar_num] = PCIBarType::Mem_32_Bit;
- break;
- case 1:
- bars_typ[bar_num] = PCIBarType::Mem_20_Bit;
- break;
- case 2:
- if (bar_num >= num_bars - 1) {
- ABORT_F("%s: BAR %d cannot be 64-bit",
- this->pci_name.c_str(), bar_num);
- }
- else if (this->bars_cfg[bar_num+1] == 0) {
- ABORT_F("%s: 64-bit BAR %d has zero for upper 32 bits",
- this->pci_name.c_str(), bar_num);
- }
- else {
- bars_typ[bar_num++] = PCIBarType::Mem_64_Bit_Lo;
- bars_typ[bar_num ] = PCIBarType::Mem_64_Bit_Hi;
- }
- break;
- default:
- ABORT_F("%s: invalid or unsupported PCI space type %d for BAR %d",
- this->pci_name.c_str(), pci_space_type, bar_num);
- } // switch pci_space_type
- }
- } // for bar_num
-}
-
-void PCIDevice::map_exp_rom_mem()
-{
- uint32_t rom_addr = this->exp_rom_bar & this->exp_bar_cfg;
- if (rom_addr) {
- if (this->exp_rom_addr != rom_addr) {
- this->unmap_exp_rom_mem();
- uint32_t rom_size = ~this->exp_bar_cfg + 1;
- this->host_instance->pci_register_mmio_region(rom_addr, rom_size, this);
- 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();
- }
+ PCIBase::pci_cfg_write(reg_offs, value, details);
}
}
diff --git a/devices/common/pci/pcidevice.h b/devices/common/pci/pcidevice.h
index 410c97b..5cb48a9 100644
--- a/devices/common/pci/pcidevice.h
+++ b/devices/common/pci/pcidevice.h
@@ -22,23 +22,10 @@ along with this program. If not, see .
#ifndef PCI_DEVICE_H
#define PCI_DEVICE_H
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
+#include
/** PCI configuration space registers offsets */
enum {
- PCI_CFG_DEV_ID = 0x00, // device and vendor IDs
- PCI_CFG_STAT_CMD = 0x04, // command/status register
- PCI_CFG_CLASS_REV = 0x08, // class code and revision ID
- PCI_CFG_DWORD_3 = 0x0C, // BIST, HeaderType, Lat_Timer and Cache_Line_Size
- PCI_CFG_BAR0 = 0x10, // base address register 0
- PCI_CFG_BAR1 = 0x14, // base address register 1
PCI_CFG_BAR2 = 0x18, // base address register 2
PCI_CFG_BAR3 = 0x1C, // base address register 3
PCI_CFG_BAR4 = 0x20, // base address register 4
@@ -46,203 +33,23 @@ enum {
PCI_CFG_CIS_PTR = 0x28, // Cardbus CIS Pointer
PCI_CFG_SUBSYS_ID = 0x2C, // Subsysten IDs
PCI_CFG_ROM_BAR = 0x30, // expansion ROM base address
- PCI_CFG_CAP_PTR = 0x34, // capabilities pointer
- PCI_CFG_DWORD_14 = 0x38, // reserved
- PCI_CFG_DWORD_15 = 0x3C, // Max_Lat, Min_Gnt, Int_Pin and Int_Line registers
};
-/** PCI Vendor IDs for devices used in Power Macintosh computers. */
-enum {
- PCI_VENDOR_ATI = 0x1002,
- PCI_VENDOR_DEC = 0x1011,
- PCI_VENDOR_MOTOROLA = 0x1057,
- PCI_VENDOR_APPLE = 0x106B,
- PCI_VENDOR_NVIDIA = 0x10DE,
-};
-
-/** PCI BAR types */
-enum PCIBarType : uint32_t {
- Unused = 0,
- Io_16_Bit,
- Io_32_Bit,
- Mem_20_Bit, // legacy type for < 1MB memory
- Mem_32_Bit,
- Mem_64_Bit_Lo,
- Mem_64_Bit_Hi
-};
-
-typedef struct {
- uint32_t bar_num;
- uint32_t bar_cfg;
-} BarConfig;
-
-class PCIDevice : public MMIODevice {
+class PCIDevice : public PCIBase {
public:
PCIDevice(std::string name);
virtual ~PCIDevice() = default;
- virtual bool supports_io_space() {
- return has_io_space;
- };
-
- /* I/O space access methods */
- virtual bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res) {
- return false;
- };
-
- virtual bool pci_io_write(uint32_t offset, uint32_t value, uint32_t size) {
- return false;
- };
-
// configuration space access 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);
- // plugin interface for using in the derived classes
- std::function pci_rd_stat;
- std::function pci_wr_stat;
- std::function pci_rd_cmd;
- std::function pci_wr_cmd;
- std::function pci_rd_bist;
- std::function pci_wr_bist;
- std::function pci_rd_lat_timer;
- std::function pci_wr_lat_timer;
- std::function pci_rd_cache_lnsz;
- std::function pci_wr_cache_lnsz;
-
- std::function pci_notify_bar_change;
-
- int attach_exp_rom_image(const std::string img_path);
-
- virtual void set_host(PCIHost* 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
- 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 set_bar_value(int bar_num, uint32_t value);
- void setup_bars(std::vector cfg_data);
- void finish_config_bars();
- void pci_wr_exp_rom_bar(uint32_t data);
- void map_exp_rom_mem();
- void unmap_exp_rom_mem();
-
- std::string pci_name; // human-readable device name
- PCIHost* host_instance; // host bridge instance to call back
-
// PCI configuration space state
- uint16_t vendor_id;
- uint16_t device_id;
- uint32_t class_rev; // class code and revision id
- uint16_t status = 0;
- uint16_t command = 0;
- uint8_t hdr_type = 0; // header type, single function
- uint8_t lat_timer = 0; // latency timer
- uint8_t cache_ln_sz = 0; // cache line size
- uint16_t subsys_id = 0;
- uint16_t subsys_vndr = 0;
- uint8_t cap_ptr = 0;
uint8_t max_lat = 0;
uint8_t min_gnt = 0;
- uint8_t irq_pin = 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_cfg[6] = { 0 }; // configuration values for base address registers
- PCIBarType bars_typ[6] = { PCIBarType::Unused }; // types for base address registers
-
- uint32_t exp_bar_cfg = 0; // expansion ROM configuration
- uint32_t exp_rom_bar = 0; // expansion ROM base address register
- uint32_t exp_rom_addr = 0; // expansion ROM base address
- uint32_t exp_rom_size = 0; // expansion ROM size in bytes
-
- std::unique_ptr exp_rom_data;
+ uint16_t subsys_id = 0;
+ uint16_t subsys_vndr = 0;
};
-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 */
diff --git a/devices/common/pci/pcihost.cpp b/devices/common/pci/pcihost.cpp
index a054b7b..3713b7f 100644
--- a/devices/common/pci/pcihost.cpp
+++ b/devices/common/pci/pcihost.cpp
@@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
-Copyright (C) 2018-22 divingkatae and maximum
+Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@@ -31,7 +31,7 @@ along with this program. If not, see .
#include
-bool PCIHost::pci_register_device(int dev_fun_num, PCIDevice* dev_instance)
+bool PCIHost::pci_register_device(int dev_fun_num, PCIBase* dev_instance)
{
// return false if dev_fun_num already registered
if (this->dev_map.count(dev_fun_num))
@@ -61,7 +61,7 @@ bool PCIHost::pci_register_device(int dev_fun_num, PCIDevice* dev_instance)
this->io_space_devs.push_back(dev_instance);
}
- PCIBridge *bridge = dynamic_cast(dev_instance);
+ PCIBridgeBase *bridge = dynamic_cast(dev_instance);
if (bridge) {
this->bridge_devs.push_back(bridge);
}
@@ -69,7 +69,7 @@ bool PCIHost::pci_register_device(int dev_fun_num, PCIDevice* dev_instance)
return true;
}
-bool PCIHost::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
+bool PCIHost::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj)
{
MemCtrlBase *mem_ctrl = dynamic_cast
(gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
@@ -77,7 +77,7 @@ bool PCIHost::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDe
return mem_ctrl->add_mmio_region(start_addr, size, obj);
}
-bool PCIHost::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
+bool PCIHost::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj)
{
MemCtrlBase *mem_ctrl = dynamic_cast
(gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
@@ -90,7 +90,7 @@ 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)
+PCIBase *PCIHost::attach_pci_device(const std::string& dev_name, int slot_id, const std::string& dev_suffix)
{
if (!DeviceRegistry::device_registered(dev_name)) {
HWComponent *hwc = dynamic_cast(this);
@@ -120,7 +120,7 @@ PCIDevice *PCIHost::attach_pci_device(const std::string& dev_name, int slot_id,
// add device to the machine object
gMachineObj->add_device(dev_name + dev_suffix, std::move(dev_obj));
- PCIDevice *dev = dynamic_cast(gMachineObj->get_comp_by_name(dev_name + dev_suffix));
+ PCIBase *dev = dynamic_cast(gMachineObj->get_comp_by_name(dev_name + dev_suffix));
// register device with the PCI host
this->pci_register_device(slot_id, dev);
@@ -182,7 +182,7 @@ void PCIHost::pci_io_write_broadcast(uint32_t offset, int size, uint32_t value)
);
}
-PCIDevice *PCIHost::pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num)
+PCIBase *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) {
diff --git a/devices/common/pci/pcihost.h b/devices/common/pci/pcihost.h
index edcca0b..db8c533 100644
--- a/devices/common/pci/pcihost.h
+++ b/devices/common/pci/pcihost.h
@@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
-Copyright (C) 2018-22 divingkatae and maximum
+Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@@ -38,7 +38,7 @@ enum {
PCI_CONFIG_TYPE = 4,
PCI_CONFIG_TYPE_0 = 0,
PCI_CONFIG_TYPE_1 = 4,
-};
+}; // PCIAccessFlags
/** PCI config space access details */
typedef struct AccessDetails {
@@ -49,8 +49,8 @@ typedef struct AccessDetails {
#define DEV_FUN(dev_num,fun_num) (((dev_num) << 3) | (fun_num))
-class PCIDevice;
-class PCIBridge;
+class PCIBase;
+class PCIBridgeBase;
class PCIHost {
public:
@@ -60,14 +60,14 @@ public:
};
~PCIHost() = default;
- virtual bool pci_register_device(int dev_fun_num, PCIDevice* dev_instance);
+ virtual bool pci_register_device(int dev_fun_num, PCIBase* dev_instance);
- 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_register_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj);
+ virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIBase* obj);
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);
+ PCIBase *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);
@@ -75,7 +75,7 @@ public:
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 PCIBase *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
virtual uint32_t pci_t1_read(uint8_t dev, uint32_t fun, uint32_t reg, AccessDetails &details) {
return 0;
@@ -85,9 +85,9 @@ public:
AccessDetails &details) {};
protected:
- std::unordered_map dev_map;
- std::vector io_space_devs;
- std::vector bridge_devs;
+ std::unordered_map dev_map;
+ std::vector io_space_devs;
+ std::vector bridge_devs;
};
// Helpers for data conversion in the PCI Configuration space.
diff --git a/devices/memctrl/mpc106.cpp b/devices/memctrl/mpc106.cpp
index 1fc83a3..77184d4 100644
--- a/devices/memctrl/mpc106.cpp
+++ b/devices/memctrl/mpc106.cpp
@@ -82,7 +82,7 @@ uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
- PCIDevice *device;
+ PCIBase *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_READ;
if (device) {
@@ -107,7 +107,7 @@ void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
- PCIDevice *device;
+ PCIBase *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_WRITE;
if (device) {
@@ -127,7 +127,7 @@ void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size
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)
+ PCIBase *&device)
{
device = NULL;
details.size = size;
diff --git a/devices/memctrl/mpc106.h b/devices/memctrl/mpc106.h
index 9c17212..05ab36d 100644
--- a/devices/memctrl/mpc106.h
+++ b/devices/memctrl/mpc106.h
@@ -91,7 +91,7 @@ protected:
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);
+ PCIBase *&device);
uint32_t config_addr;
diff --git a/machines/machineyosemite.cpp b/machines/machineyosemite.cpp
index c319432..7daa011 100644
--- a/machines/machineyosemite.cpp
+++ b/machines/machineyosemite.cpp
@@ -54,7 +54,7 @@ int initialize_yosemite(std::string& id)
// connect PCI devices
grackle_obj->pci_register_device(DEV_FUN(13,0),
- dynamic_cast(gMachineObj->get_comp_by_name("Dec21154")));
+ dynamic_cast(gMachineObj->get_comp_by_name("Dec21154")));
sec_bridge->pci_register_device(DEV_FUN(5,0),
dynamic_cast(gMachineObj->get_comp_by_name("Heathrow")));