From 6d23e18c11dfa530f4ec9893f18b7905ca90e9af Mon Sep 17 00:00:00 2001 From: joevt Date: Thu, 8 Jun 2023 07:09:29 -0700 Subject: [PATCH] pci: Add PCI CardBus bridge. PCCard is used by PowerBook G3 Wallstreet in Open Firmware 2.0.1. CardBus is probed in New World Macs starting from at least Open Firmware 4.1.9f1 sometime after Open Firmware 3.1.1. - Create PCIBase from common stuff in PCIDevice. - Add PCIBridgeBase. These have a primary bus number, secondary bus number, and subordinate bus number which are used to determine if PCI type 1 config cycle should be passed. - Change PCIBridge to use PCIBridgeBase instead of PCIDevice. - Add PCICardBusBridge which uses PCIBridgeBase. --- devices/common/pci/bandit.cpp | 6 +- devices/common/pci/bandit.h | 2 +- devices/common/pci/pcibase.cpp | 304 ++++++++++++++++++++++++ devices/common/pci/pcibase.h | 245 +++++++++++++++++++ devices/common/pci/pcibridge.cpp | 74 +----- devices/common/pci/pcibridge.h | 46 +--- devices/common/pci/pcibridgebase.cpp | 87 +++++++ devices/common/pci/pcibridgebase.h | 80 +++++++ devices/common/pci/pcicardbusbridge.cpp | 223 +++++++++++++++++ devices/common/pci/pcicardbusbridge.h | 112 +++++++++ devices/common/pci/pcidevice.cpp | 258 +------------------- devices/common/pci/pcidevice.h | 201 +--------------- devices/common/pci/pcihost.cpp | 16 +- devices/common/pci/pcihost.h | 26 +- devices/memctrl/mpc106.cpp | 6 +- devices/memctrl/mpc106.h | 2 +- machines/machineyosemite.cpp | 2 +- 17 files changed, 1108 insertions(+), 582 deletions(-) create mode 100644 devices/common/pci/pcibase.cpp create mode 100644 devices/common/pci/pcibase.h create mode 100644 devices/common/pci/pcibridgebase.cpp create mode 100644 devices/common/pci/pcibridgebase.h create mode 100644 devices/common/pci/pcicardbusbridge.cpp create mode 100644 devices/common/pci/pcicardbusbridge.h 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")));