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")));