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.
This commit is contained in:
joevt 2023-06-08 07:09:29 -07:00 committed by dingusdev
parent 5f8e7fcb73
commit 6d23e18c11
17 changed files with 1108 additions and 582 deletions

View File

@ -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 &reg_offs,
AccessDetails &details, PCIDevice *&device)
AccessDetails &details, PCIBase *&device)
{
device = NULL;
details.size = size;

View File

@ -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 &reg_offs, AccessDetails &details,
PCIDevice *&device);
PCIBase *&device);
};
/*

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <devices/common/pci/pcibase.h>
#include <endianswap.h>
#include <loguru.hpp>
#include <memaccess.h>
#include <cinttypes>
#include <fstream>
#include <cstring>
#include <string>
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<BarConfig> 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<uint8_t[]> (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();
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef PCI_BASE_H
#define PCI_BASE_H
#include <devices/common/mmiodevice.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
#include <vector>
/** 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<uint16_t()> pci_rd_stat;
std::function<void(uint16_t)> pci_wr_stat;
std::function<uint16_t()> pci_rd_cmd;
std::function<void(uint16_t)> pci_wr_cmd;
std::function<uint8_t()> pci_rd_bist;
std::function<void(uint8_t)> pci_wr_bist;
std::function<uint8_t()> pci_rd_lat_timer;
std::function<void(uint8_t)> pci_wr_lat_timer;
std::function<uint8_t()> pci_rd_cache_lnsz;
std::function<void(uint8_t)> pci_wr_cache_lnsz;
std::function<void(int)> 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<BarConfig> 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<uint8_t[]> 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 */

View File

@ -24,16 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/pci/pcihost.h>
#include <loguru.hpp>
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;
}
}

View File

@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef PCI_BRIDGE_H
#define PCI_BRIDGE_H
#include <devices/common/pci/pcidevice.h>
#include <devices/common/pci/pcibridgebase.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
@ -30,52 +30,28 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** 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<uint8_t()> pci_rd_primary_bus;
std::function<void(uint8_t)> pci_wr_primary_bus;
std::function<uint8_t()> pci_rd_secondary_bus;
std::function<void(uint8_t)> pci_wr_secondary_bus;
std::function<uint8_t()> pci_rd_subordinate_bus;
std::function<void(uint8_t)> pci_wr_subordinate_bus;
std::function<uint8_t()> pci_rd_sec_latency_timer;
std::function<void(uint8_t)> pci_wr_sec_latency_timer;
std::function<uint16_t()> pci_rd_sec_status;
std::function<void(uint16_t)> pci_wr_sec_status;
std::function<uint8_t()> pci_rd_io_base;
std::function<void(uint8_t)> pci_wr_io_base;
std::function<uint8_t()> pci_rd_io_limit;
@ -96,16 +72,9 @@ public:
std::function<void(uint16_t)> pci_wr_io_base_upper16;
std::function<uint16_t()> pci_rd_io_limit_upper16;
std::function<void(uint16_t)> pci_wr_io_limit_upper16;
std::function<uint16_t()> pci_rd_bridge_control;
std::function<void(uint16_t)> pci_wr_bridge_control;
protected:
// PCI configuration space state
uint8_t primary_bus = 0;
uint8_t secondary_bus = 0;
uint8_t subordinate_bus = 0;
uint8_t sec_latency_timer = 0; // if supportss r/w then must reset to 0
uint16_t sec_status = 0;
uint8_t io_base = 0;
uint8_t io_limit = 0;
uint16_t memory_base = 0;
@ -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 */

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <devices/common/pci/pcibridgebase.h>
#include <memaccess.h>
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);
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef PCI_BRIDGE_BASE_H
#define PCI_BRIDGE_BASE_H
#include <devices/deviceregistry.h>
#include <devices/common/pci/pcibase.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
#include <string>
#include <unordered_map>
#include <vector>
/** 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<uint8_t()> pci_rd_primary_bus;
std::function<void(uint8_t)> pci_wr_primary_bus;
std::function<uint8_t()> pci_rd_secondary_bus;
std::function<void(uint8_t)> pci_wr_secondary_bus;
std::function<uint8_t()> pci_rd_subordinate_bus;
std::function<void(uint8_t)> pci_wr_subordinate_bus;
std::function<uint8_t()> pci_rd_sec_latency_timer;
std::function<void(uint8_t)> pci_wr_sec_latency_timer;
std::function<uint16_t()> pci_rd_sec_status;
std::function<void(uint16_t)> pci_wr_sec_status;
std::function<uint16_t()> pci_rd_bridge_control;
std::function<void(uint16_t)> pci_wr_bridge_control;
protected:
// PCI configuration space state
uint8_t primary_bus = 0;
uint8_t secondary_bus = 0;
uint8_t subordinate_bus = 0;
uint8_t sec_latency_timer = 0; // if supportss r/w then must reset to 0
uint16_t sec_status = 0;
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 */

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <devices/common/pci/pcicardbusbridge.h>
#include <memaccess.h>
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);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef PCI_CARDBUSBRIDGE_H
#define PCI_CARDBUSBRIDGE_H
#include <devices/deviceregistry.h>
#include <devices/common/pci/pcibridgebase.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
#include <string>
#include <unordered_map>
#include <vector>
/** 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<uint32_t()> pci_rd_memory_base_0;
std::function<void(uint32_t)> pci_wr_memory_base_0;
std::function<uint32_t()> pci_rd_memory_limit_0;
std::function<void(uint32_t)> pci_wr_memory_limit_0;
std::function<uint32_t()> pci_rd_memory_base_1;
std::function<void(uint32_t)> pci_wr_memory_base_1;
std::function<uint32_t()> pci_rd_memory_limit_1;
std::function<void(uint32_t)> pci_wr_memory_limit_1;
std::function<uint32_t()> pci_rd_io_base_0;
std::function<void(uint32_t)> pci_wr_io_base_0;
std::function<uint32_t()> pci_rd_io_limit_0;
std::function<void(uint32_t)> pci_wr_io_limit_0;
std::function<uint32_t()> pci_rd_io_base_1;
std::function<void(uint32_t)> pci_wr_io_base_1;
std::function<uint32_t()> pci_rd_io_limit_1;
std::function<void(uint32_t)> 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 */

View File

@ -27,38 +27,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstring>
#include <string>
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<BarConfig> 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<uint8_t[]> (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);
}
}

View File

@ -22,23 +22,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef PCI_DEVICE_H
#define PCI_DEVICE_H
#include <devices/common/mmiodevice.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <devices/common/pci/pcibase.h>
/** 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<uint16_t()> pci_rd_stat;
std::function<void(uint16_t)> pci_wr_stat;
std::function<uint16_t()> pci_rd_cmd;
std::function<void(uint16_t)> pci_wr_cmd;
std::function<uint8_t()> pci_rd_bist;
std::function<void(uint8_t)> pci_wr_bist;
std::function<uint8_t()> pci_rd_lat_timer;
std::function<void(uint8_t)> pci_wr_lat_timer;
std::function<uint8_t()> pci_rd_cache_lnsz;
std::function<void(uint8_t)> pci_wr_cache_lnsz;
std::function<void(int)> 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<BarConfig> 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<uint8_t[]> 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 */

View File

@ -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 <https://www.gnu.org/licenses/>.
#include <cinttypes>
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<PCIBridge*>(dev_instance);
PCIBridgeBase *bridge = dynamic_cast<PCIBridgeBase*>(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<MemCtrlBase *>
(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<MemCtrlBase *>
(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<HWComponent*>(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<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name + dev_suffix));
PCIBase *dev = dynamic_cast<PCIBase*>(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) {

View File

@ -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<int, PCIDevice*> dev_map;
std::vector<PCIDevice*> io_space_devs;
std::vector<PCIBridge*> bridge_devs;
std::unordered_map<int, PCIBase*> dev_map;
std::vector<PCIBase*> io_space_devs;
std::vector<PCIBridgeBase*> bridge_devs;
};
// Helpers for data conversion in the PCI Configuration space.

View File

@ -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 &reg_offs, AccessDetails &details,
PCIDevice *&device)
PCIBase *&device)
{
device = NULL;
details.size = size;

View File

@ -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 &reg_offs, AccessDetails &details,
PCIDevice *&device);
PCIBase *&device);
uint32_t config_addr;

View File

@ -54,7 +54,7 @@ int initialize_yosemite(std::string& id)
// connect PCI devices
grackle_obj->pci_register_device(DEV_FUN(13,0),
dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Dec21154")));
dynamic_cast<PCIBase*>(gMachineObj->get_comp_by_name("Dec21154")));
sec_bridge->pci_register_device(DEV_FUN(5,0),
dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));