Merge pull request #43 from joevt/PCI-bridge-and-multi-function-support

Pci bridge and multi function support
This commit is contained in:
Maxim Poliakovski 2023-02-07 00:41:50 +01:00 committed by GitHub
commit 8231ecd466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 627 additions and 223 deletions

View File

@ -139,101 +139,50 @@ void BanditPciDevice::verbose_address_space()
uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
{
uint32_t idsel, result;
switch (offset >> 22) {
case 3: // CONFIG_DATA
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
LOG_F(
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x",
this->name.c_str(), BUS_NUM(), DEV_NUM(), FUN_NUM(), REG_NUM() + (offset & 3)
);
return 0xFFFFFFFFUL; // PCI spec §6.1
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
PCIDevice *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_READ;
if (device) {
return pci_conv_rd_data(device->pci_cfg_read(reg_offs, details), details);
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
this->name.c_str(), (uint32_t)this->config_addr);
LOG_READ_NON_EXISTENT_PCI_DEVICE();
return 0xFFFFFFFFUL; // PCI spec §6.1
}
if (this->dev_map.count(idsel)) {
AccessDetails details;
details.offset = offset & 3;
details.size = size;
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_READ;
result = this->dev_map[idsel]->pci_cfg_read(REG_NUM(), details);
return pci_conv_rd_data(result, details);
} else {
LOG_F(
ERROR, "%s err: read attempt from non-existing PCI device ??:%02x.%x @%02x",
this->name.c_str(), WHAT_BIT_SET(idsel) + 11, FUN_NUM(), REG_NUM() + (offset & 3)
);
return 0xFFFFFFFFUL; // PCI spec §6.1
}
break;
case 2: // CONFIG_ADDR
return BYTESWAP_32(this->config_addr);
default: // I/O space
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_read(offset, size, &result))
return result;
}
LOG_F(ERROR, "%s: attempt to read from unmapped PCI I/O space, offset=0x%X",
this->name.c_str(), offset);
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
return 0;
return pci_io_read_broadcast(offset, size);
}
}
void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
uint32_t idsel;
switch (offset >> 22) {
case 3: // CONFIG_DATA
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
LOG_F(
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x",
this->name.c_str(), BUS_NUM(), DEV_NUM(), FUN_NUM(), REG_NUM() + (offset & 3)
);
return;
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
this->name.c_str(), (uint32_t)this->config_addr);
return;
}
if (this->dev_map.count(idsel)) {
AccessDetails details;
details.offset = offset & 3;
details.size = size;
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_WRITE;
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
PCIDevice *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_WRITE;
if (device) {
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
this->dev_map[idsel]->pci_cfg_write(REG_NUM(), BYTESWAP_32(value), details);
} else { // otherwise perform necessary data transformations -> slow path
uint32_t old_val = this->dev_map[idsel]->pci_cfg_read(REG_NUM(), details);
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
this->dev_map[idsel]->pci_cfg_write(REG_NUM(), new_val, details);
device->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
return;
}
} else {
LOG_F(
ERROR, "%s err: write attempt to non-existing PCI device ??:%02x.%x @%02x",
this->name.c_str(), WHAT_BIT_SET(idsel) + 11, FUN_NUM(), REG_NUM() + (offset & 3)
);
// otherwise perform necessary data transformations -> slow path
uint32_t old_val = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
device->pci_cfg_write(reg_offs, new_val, details);
return;
}
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
break;
case 2: // CONFIG_ADDR
@ -241,14 +190,35 @@ void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
break;
default: // I/O space
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request handled"
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_write(offset, value, size))
return;
}
LOG_F(ERROR, "%s: attempt to read from unmapped PCI I/O space, offset=0x%X",
this->name.c_str(), offset);
pci_io_write_broadcast(offset, size, value);
}
}
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)
{
device = NULL;
details.size = size;
details.offset = offset & 3;
fun_num = FUN_NUM();
reg_offs = REG_NUM();
if (this->config_addr & BANDIT_CAR_TYPE) { // type 1 configuration command
details.flags = PCI_CONFIG_TYPE_1;
bus_num = BUS_NUM();
dev_num = DEV_NUM();
device = pci_find_device(bus_num, dev_num, fun_num);
return;
}
details.flags = PCI_CONFIG_TYPE_0;
bus_num = 0; // bus number is meaningless for type 0 configuration command; a type 1 configuration command cannot reach devices attached directly to the host
uint32_t idsel = this->config_addr & 0xFFFFF800U;
if (!SINGLE_BIT_SET(idsel)) {
for (dev_num = -1, idsel = this->config_addr; idsel; idsel >>= 1, dev_num++) {}
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL", this->name.c_str(), (uint32_t)this->config_addr);
return;
}
dev_num = WHAT_BIT_SET(idsel);
if (this->dev_map.count(DEV_FUN(dev_num, fun_num))) {
device = this->dev_map[DEV_FUN(dev_num, fun_num)];
}
}
@ -276,7 +246,7 @@ Bandit::Bandit(int bridge_num, std::string name, int dev_id, int rev)
this->my_pci_device = unique_ptr<BanditPciDevice>(
new BanditPciDevice(bridge_num, name, dev_id, rev)
);
this->pci_register_device(1, this->my_pci_device.get());
this->pci_register_device(DEV_FUN(BANDIT_DEV,0), this->my_pci_device.get());
}
Chaos::Chaos(std::string name) : BanditHost()

View File

@ -46,6 +46,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <memory>
#include <string>
#define BANDIT_DEV (11) // Bandit's own device number
#define BANDIT_CAR_TYPE (1 << 0) // Bandit config address type bit
/* Convenient macros for parsing CONFIG_ADDR fields. */
@ -75,6 +76,9 @@ public:
protected:
uint32_t config_addr;
private:
inline void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num, int &fun_num, uint8_t &reg_offs, AccessDetails &details, PCIDevice *&device);
};
/*

View File

@ -0,0 +1,168 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-22 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <devices/common/pci/pcibridge.h>
#include <memaccess.h>
PCIBridge::PCIBridge(std::string name) : PCIDevice(name)
{
this->num_bars = 2;
this->hdr_type = 1;
this->pci_rd_primary_bus = [this]() { return this->primary_bus; };
this->pci_rd_secondary_bus = [this]() { return this->secondary_bus; };
this->pci_rd_subordinate_bus = [this]() { return this->subordinate_bus; };
this->pci_rd_sec_latency_timer = [this]() { return this->sec_latency_timer; };
this->pci_rd_sec_status = [this]() { return this->sec_status; };
this->pci_rd_memory_base = [this]() { return this->memory_base; };
this->pci_rd_memory_limit = [this]() { return this->memory_limit; };
this->pci_rd_io_base = [this]() { return this->io_base; };
this->pci_rd_io_limit = [this]() { return this->io_limit; };
this->pci_rd_pref_mem_base = [this]() { return this->pref_mem_base; };
this->pci_rd_pref_mem_limit = [this]() { return this->pref_mem_limit; };
this->pci_rd_io_base_upper16 = [this]() { return this->io_base_upper16; };
this->pci_rd_io_limit_upper16 = [this]() { return this->io_limit_upper16; };
this->pci_rd_pref_base_upper32 = [this]() { return this->pref_base_upper32; };
this->pci_rd_pref_limit_upper32 = [this]() { return this->pref_limit_upper32; };
this->pci_rd_bridge_control = [this]() { return this->bridge_control; };
this->pci_wr_primary_bus = [this](uint8_t val) { this->primary_bus = val; };
this->pci_wr_secondary_bus = [this](uint8_t val) { this->secondary_bus = val; };
this->pci_wr_subordinate_bus = [this](uint8_t val) { this->subordinate_bus = val; };
this->pci_wr_sec_latency_timer = [this](uint8_t val) { this->sec_latency_timer = (this->sec_latency_timer & ~this->sec_latency_timer_cfg) | (val & this->sec_latency_timer_cfg); };
this->pci_wr_sec_status = [this](uint16_t val) {};
this->pci_wr_memory_base = [this](uint16_t val) { this->memory_base = (val & this->memory_cfg ) | (this->memory_cfg & 15); this->memory_base_32 = ((this->memory_base & 0xfff0) << 16) ; };
this->pci_wr_memory_limit = [this](uint16_t val) { this->memory_limit = (val & this->memory_cfg ) | (this->memory_cfg & 15); this->memory_limit_32 = ((this->memory_limit & 0xfff0) << 16) + 0x100000; };
this->pci_wr_io_base = [this](uint8_t val) { this->io_base = (val & this->io_cfg ) | (this->io_cfg & 15); this->io_base_32 = ((uint32_t)this->io_base_upper16 << 16) | ((this->io_base & 0xf0) << 8) ; };
this->pci_wr_io_limit = [this](uint8_t val) { this->io_limit = (val & this->io_cfg ) | (this->io_cfg & 15); this->io_limit_32 = (((uint32_t)this->io_limit_upper16 << 16) | ((this->io_limit & 0xf0) << 8)) + 0x1000 ; };
this->pci_wr_pref_mem_base = [this](uint16_t val) { this->pref_mem_base = (val & this->pref_mem_cfg) | (this->pref_mem_cfg & 15); this->pref_mem_base_64 = ((uint64_t)this->pref_base_upper32 << 32) | ((this->pref_mem_base & 0xfff0) << 16) ; };
this->pci_wr_pref_mem_limit = [this](uint16_t val) { this->pref_mem_limit = (val & this->pref_mem_cfg) | (this->pref_mem_cfg & 15); this->pref_mem_limit_64 = (((uint64_t)this->pref_limit_upper32 << 32) | ((this->pref_mem_limit & 0xfff0) << 16)) + 0x100000; };
this->pci_wr_io_base_upper16 = [this](uint16_t val) { if ((this->io_base & 15) == 1) this->io_base_upper16 = val; { this->io_base_32 = ((uint32_t)this->io_base_upper16 << 16) | ((this->io_base & 0xf0) << 8) ; } };
this->pci_wr_io_limit_upper16 = [this](uint16_t val) { if ((this->io_limit & 15) == 1) this->io_limit_upper16 = val; { this->io_limit_32 = (((uint32_t)this->io_limit_upper16 << 16) | ((this->io_limit & 0xf0) << 8)) + 0x1000 ; } };
this->pci_wr_pref_base_upper32 = [this](uint32_t val) { if ((this->pref_mem_cfg & 15) == 1) this->pref_base_upper32 = val; { this->pref_mem_base_64 = ((uint64_t)this->pref_base_upper32 << 32) | ((this->pref_mem_base & 0xfff0) << 16) ; } };
this->pci_wr_pref_limit_upper32 = [this](uint32_t val) { if ((this->pref_mem_cfg & 15) == 1) this->pref_limit_upper32 = val; { this->pref_mem_limit_64 = (((uint64_t)this->pref_limit_upper32 << 32) | ((this->pref_mem_limit & 0xfff0) << 16)) + 0x100000; } };
this->pci_wr_bridge_control = [this](uint16_t val) { this->bridge_control = val; };
};
bool PCIBridge::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
{
// FIXME: constrain region to memory range
return this->host_instance->pci_register_mmio_region(start_addr, size, obj);
}
bool PCIBridge::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj)
{
return this->host_instance->pci_unregister_mmio_region(start_addr, size, obj);
}
uint32_t PCIBridge::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
{
if (reg_offs < 0x18) {
return PCIDevice::pci_cfg_read(reg_offs, details);
}
switch (reg_offs) {
case PCI_CFG_PRIMARY_BUS:
return (this->pci_rd_sec_latency_timer() << 24) | (this->pci_rd_subordinate_bus() << 16) | (this->pci_rd_secondary_bus() << 8) | (this->pci_rd_primary_bus());
case PCI_CFG_IO_BASE:
return (this->pci_rd_sec_status() << 16) | (this->pci_rd_io_limit() << 8) | (this->pci_rd_io_base());
case PCI_CFG_MEMORY_BASE:
return (this->pci_rd_memory_limit() << 16) | (this->pci_rd_memory_base());
case PCI_CFG_PREF_MEM_BASE:
return (this->pci_rd_pref_mem_limit() << 16) | (this->pci_rd_pref_mem_base());
case PCI_CFG_PREF_BASE_UPPER32:
return this->pci_rd_pref_base_upper32();
case PCI_CFG_PREF_LIMIT_UPPER32:
return this->pci_rd_pref_limit_upper32();
case PCI_CFG_IO_BASE_UPPER16:
return (this->pci_rd_io_limit_upper16() << 16) | (this->pci_rd_io_base_upper16());
case PCI_CFG_CAP_PTR:
return cap_ptr;
case PCI_CFG_BRIDGE_ROM_ADDRESS:
return exp_rom_bar;
case PCI_CFG_INTERRUPT_LINE:
return (this->pci_rd_bridge_control() << 16) | (irq_pin << 8) | irq_line;
}
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
return 0;
}
void PCIBridge::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
{
if (reg_offs < 0x18) {
return PCIDevice::pci_cfg_write(reg_offs, value, details);
}
switch (reg_offs) {
case PCI_CFG_PRIMARY_BUS:
this->pci_wr_sec_latency_timer(value >> 24);
this->pci_wr_subordinate_bus(value >> 16);
this->pci_wr_secondary_bus(value >> 8);
this->pci_wr_primary_bus(value & 0xFFU);
break;
case PCI_CFG_IO_BASE:
this->pci_wr_sec_status(value >> 16);
this->pci_wr_io_limit(value >> 8);
this->pci_wr_io_base(value & 0xFFU);
break;
case PCI_CFG_MEMORY_BASE:
this->pci_wr_memory_limit(value >> 16);
this->pci_wr_memory_base(value & 0xFFFFU);
break;
case PCI_CFG_PREF_MEM_BASE:
this->pci_wr_pref_mem_limit(value >> 16);
this->pci_wr_pref_mem_base(value & 0xFFFFU);
break;
case PCI_CFG_PREF_BASE_UPPER32:
this->pci_wr_pref_base_upper32(value);
break;
case PCI_CFG_PREF_LIMIT_UPPER32:
this->pci_wr_pref_limit_upper32(value);
break;
case PCI_CFG_IO_BASE_UPPER16:
this->pci_wr_io_limit_upper16(value >> 16);
this->pci_wr_io_base_upper16(value & 0xFFFFU);
break;
case PCI_CFG_BRIDGE_ROM_ADDRESS:
this->pci_wr_exp_rom_bar(value);
break;
case PCI_CFG_INTERRUPT_LINE:
this->irq_line = value >> 24;
this->pci_wr_bridge_control(value >> 16);
break;
default:
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
}
}
bool PCIBridge::pci_io_read(uint32_t offset, uint32_t size, uint32_t* res)
{
if (!(this->command & 1)) return false;
if (offset < this->io_base_32 || offset + size >= this->io_limit_32) return false;
return this->pci_io_read_loop(offset, size, *res);
}
bool PCIBridge::pci_io_write(uint32_t offset, uint32_t value, uint32_t size)
{
if (!(this->command & 1)) return false;
if (offset < this->io_base_32 || offset + size >= this->io_limit_32) return false;
return this->pci_io_read_loop(offset, size, value);
}

View File

@ -0,0 +1,139 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-22 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef PCI_BRIDGE_H
#define PCI_BRIDGE_H
#include <devices/deviceregistry.h>
#include <devices/common/pci/pcidevice.h>
#include <devices/common/pci/pcihost.h>
#include <cinttypes>
#include <string>
#include <unordered_map>
#include <vector>
/** PCI configuration space registers offsets */
enum {
PCI_CFG_PRIMARY_BUS = 0x18, // PRIMARY_BUS, SECONDARY_BUS, SUBORDINATE_BUS, SEC_LATENCY_TIMER
PCI_CFG_IO_BASE = 0x1C, // IO_BASE.b, IO_LIMIT.b, SEC_STATUS.w
PCI_CFG_MEMORY_BASE = 0x20, // MEMORY_BASE.w, MEMORY_LIMIT.w
PCI_CFG_PREF_MEM_BASE = 0x24, // PREF_MEMORY_BASE.w, PREF_MEMORY_LIMIT.w
PCI_CFG_PREF_BASE_UPPER32 = 0x28, // PREF_BASE_UPPER32
PCI_CFG_PREF_LIMIT_UPPER32 = 0x2c, // PREF_LIMIT_UPPER32
PCI_CFG_IO_BASE_UPPER16 = 0x30, // IO_BASE_UPPER16.w, IO_LIMIT_UPPER16.w
// PCI_CFG_CAP_PTR
PCI_CFG_BRIDGE_ROM_ADDRESS = 0x38, // BRIDGE_ROM_ADDRESS
PCI_CFG_INTERRUPT_LINE = 0x3C, // INTERRUPT_LINE.b, INTERRUPT_PIN.b, BRIDGE_CONTROL.w
};
class PCIBridge : public PCIHost, public PCIDevice {
friend class PCIHost;
public:
PCIBridge(std::string name);
~PCIBridge() = default;
// PCIHost methods
virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
// PCIDevice methods
virtual uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
virtual void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
virtual bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res);
virtual bool pci_io_write(uint32_t offset, uint32_t value, uint32_t size);
// MMIODevice methods
virtual uint32_t read(uint32_t rgn_start, uint32_t offset, int size) {
return 0;
};
virtual void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) { };
// plugin interface for using in the derived classes
std::function<uint8_t()> pci_rd_primary_bus;
std::function<void(uint8_t)> pci_wr_primary_bus;
std::function<uint8_t()> pci_rd_secondary_bus;
std::function<void(uint8_t)> pci_wr_secondary_bus;
std::function<uint8_t()> pci_rd_subordinate_bus;
std::function<void(uint8_t)> pci_wr_subordinate_bus;
std::function<uint8_t()> pci_rd_sec_latency_timer;
std::function<void(uint8_t)> pci_wr_sec_latency_timer;
std::function<uint16_t()> pci_rd_sec_status;
std::function<void(uint16_t)> pci_wr_sec_status;
std::function<uint8_t()> pci_rd_io_base;
std::function<void(uint8_t)> pci_wr_io_base;
std::function<uint8_t()> pci_rd_io_limit;
std::function<void(uint8_t)> pci_wr_io_limit;
std::function<uint16_t()> pci_rd_memory_base;
std::function<void(uint16_t)> pci_wr_memory_base;
std::function<uint16_t()> pci_rd_memory_limit;
std::function<void(uint16_t)> pci_wr_memory_limit;
std::function<uint16_t()> pci_rd_pref_mem_base;
std::function<void(uint16_t)> pci_wr_pref_mem_base;
std::function<uint16_t()> pci_rd_pref_mem_limit;
std::function<void(uint16_t)> pci_wr_pref_mem_limit;
std::function<uint32_t()> pci_rd_pref_base_upper32;
std::function<void(uint32_t)> pci_wr_pref_base_upper32;
std::function<uint32_t()> pci_rd_pref_limit_upper32;
std::function<void(uint32_t)> pci_wr_pref_limit_upper32;
std::function<uint16_t()> pci_rd_io_base_upper16;
std::function<void(uint16_t)> pci_wr_io_base_upper16;
std::function<uint16_t()> pci_rd_io_limit_upper16;
std::function<void(uint16_t)> pci_wr_io_limit_upper16;
std::function<uint16_t()> pci_rd_bridge_control;
std::function<void(uint16_t)> pci_wr_bridge_control;
protected:
// PCI configuration space state
uint8_t primary_bus = 0;
uint8_t secondary_bus = 0;
uint8_t subordinate_bus = 0;
uint8_t sec_latency_timer = 0; // if supportss r/w then must reset to 0
uint16_t sec_status = 0;
uint8_t io_base = 0;
uint8_t io_limit = 0;
uint16_t memory_base = 0;
uint16_t memory_limit = 0;
uint16_t pref_mem_base = 0;
uint16_t pref_mem_limit = 0;
uint32_t pref_base_upper32 = 0;
uint32_t pref_limit_upper32 = 0;
uint16_t io_base_upper16 = 0;
uint16_t io_limit_upper16 = 0;
uint16_t bridge_control = 0;
uint8_t sec_latency_timer_cfg = 0; // 0 = not writable, 0xf8 = limits the granularity to eight PCI clocks
uint8_t io_cfg = 0xf0; // 0 = not writable, 0xf0 = supports 16 bit io range, 0xf1 = supports 32 bit io range
uint16_t memory_cfg = 0xfff0; // 0 = not writable, 0xfff0 = supports 32 bit memory range
uint16_t pref_mem_cfg = 0xfff0; // 0 = not writable, 0xfff0 = supports 32 bit prefetchable memory range, 0xfff1 = supports 64 bit prefetchable memory range
uint32_t io_base_32 = 0;
uint32_t io_limit_32 = 0;
uint64_t memory_base_32 = 0;
uint64_t memory_limit_32 = 0;
uint64_t pref_mem_base_64 = 0;
uint64_t pref_mem_limit_64 = 0;
private:
};
#endif /* PCI_BRIDGE_H */

View File

@ -20,7 +20,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <devices/common/pci/pcidevice.h>
#include <devices/common/viacuda.h>
#include <endianswap.h>
#include <loguru.hpp>
#include <memaccess.h>
@ -103,17 +102,7 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &
this->set_bar_value((reg_offs - 0x10) >> 2, value);
break;
case PCI_CFG_ROM_BAR:
if ((value & this->exp_bar_cfg) == this->exp_bar_cfg) {
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
} else {
this->exp_rom_bar = (value & (this->exp_bar_cfg | 1));
if (this->exp_rom_bar & 1) {
this->map_exp_rom_mem();
} else {
LOG_F(WARNING, "%s: unmapping of expansion ROM not implemented yet",
this->pci_name.c_str());
}
}
this->pci_wr_exp_rom_bar(value);
break;
case PCI_CFG_DWORD_15:
this->irq_line = value >> 24;
@ -242,7 +231,7 @@ void PCIDevice::set_bar_value(int bar_num, uint32_t value)
void PCIDevice::finish_config_bars()
{
for (int bar_num = 0; bar_num < 6; bar_num++) {
for (int bar_num = 0; bar_num < this->num_bars; bar_num++) {
uint32_t bar_cfg = this->bars_cfg[bar_num];
if (!bar_cfg) // skip unimplemented BARs
@ -251,6 +240,7 @@ void PCIDevice::finish_config_bars()
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;
@ -262,7 +252,7 @@ void PCIDevice::finish_config_bars()
bars_typ[bar_num] = PCIBarType::Mem_20_Bit;
break;
case 2:
if (bar_num >= 5) {
if (bar_num >= num_bars - 1) {
ABORT_F("%s: BAR %d cannot be 64-bit",
this->pci_name.c_str(), bar_num);
}
@ -271,8 +261,8 @@ void PCIDevice::finish_config_bars()
this->pci_name.c_str(), bar_num);
}
else {
bars_typ[bar_num] = PCIBarType::Mem_64_Bit_Hi;
bars_typ[bar_num++] = PCIBarType::Mem_64_Bit_Lo;
bars_typ[bar_num ] = PCIBarType::Mem_64_Bit_Hi;
}
break;
default:
@ -285,13 +275,45 @@ void PCIDevice::finish_config_bars()
void PCIDevice::map_exp_rom_mem()
{
uint32_t rom_addr, rom_size;
rom_addr = this->exp_rom_bar & this->exp_bar_cfg;
rom_size = ~this->exp_bar_cfg + 1;
if (!this->exp_rom_addr || this->exp_rom_addr != rom_addr) {
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();
}
}
}

View File

@ -81,7 +81,7 @@ public:
virtual ~PCIDevice() = default;
virtual bool supports_io_space() {
return false;
return has_io_space;
};
/* I/O space access methods */
@ -117,6 +117,13 @@ public:
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) { }
@ -125,7 +132,9 @@ 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
@ -136,7 +145,7 @@ protected:
uint32_t class_rev; // class code and revision id
uint16_t status = 0;
uint16_t command = 0;
uint8_t hdr_type = 0; // header type
uint8_t hdr_type = 0; // header type, single function
uint8_t lat_timer = 0; // latency timer
uint8_t cache_ln_sz = 0; // cache line size
uint16_t subsys_id = 0;
@ -147,6 +156,8 @@ protected:
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

View File

@ -20,28 +20,50 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <devices/common/hwcomponent.h>
#include <devices/common/pci/pcidevice.h>
#include <devices/common/pci/pcibridge.h>
#include <devices/common/pci/pcihost.h>
#include <devices/memctrl/memctrlbase.h>
#include <machines/machinebase.h>
#include <endianswap.h>
#include <loguru.hpp>
#include <cinttypes>
bool PCIHost::pci_register_device(int dev_num, PCIDevice* dev_instance)
bool PCIHost::pci_register_device(int dev_fun_num, PCIDevice* dev_instance)
{
// return false if dev_num already registered
if (this->dev_map.count(dev_num))
// return false if dev_fun_num already registered
if (this->dev_map.count(dev_fun_num))
return false;
this->dev_map[dev_num] = dev_instance;
int fun_num = dev_fun_num & 7;
int dev_num = (dev_fun_num >> 3) & 0x1f;
bool is_multi_function = fun_num != 0;
for (int other_fun_num = 0; other_fun_num < 8; other_fun_num++) {
if (this->dev_map.count(DEV_FUN(dev_num, other_fun_num))) {
is_multi_function = true;
if (is_multi_function && other_fun_num == 0) {
this->dev_map[DEV_FUN(dev_num, other_fun_num)]->set_multi_function(true);
}
}
}
this->dev_map[dev_fun_num] = dev_instance;
dev_instance->set_host(this);
if (is_multi_function && fun_num == 0) {
dev_instance->set_multi_function(true);
}
if (dev_instance->supports_io_space()) {
this->io_space_devs.push_back(dev_instance);
}
PCIBridge *bridge = dynamic_cast<PCIBridge*>(dev_instance);
if (bridge) {
this->bridge_devs.push_back(bridge);
}
return true;
}
@ -61,30 +83,113 @@ bool PCIHost::pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCI
return mem_ctrl->remove_mmio_region(start_addr, size, obj);
}
void PCIHost::attach_pci_device(std::string& dev_name, int slot_id)
void PCIHost::attach_pci_device(const std::string& dev_name, int slot_id)
{
this->attach_pci_device(dev_name, slot_id, "");
}
PCIDevice *PCIHost::attach_pci_device(const std::string& dev_name, int slot_id, const std::string& dev_suffix)
{
if (!DeviceRegistry::device_registered(dev_name)) {
LOG_F(WARNING, "PCIHost: specified PCI device %s doesn't exist", dev_name.c_str());
return;
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
LOG_F(
WARNING, "%s: specified PCI device %s doesn't exist",
hwc ? hwc->get_name().c_str() : "PCIHost", dev_name.c_str()
);
return NULL;
}
// attempt to create device object
auto dev_obj = DeviceRegistry::get_descriptor(dev_name).m_create_func();
if (!dev_obj->supports_type(HWCompType::PCI_DEV)) {
LOG_F(WARNING, "PCIHost: cannot attach non-PCI device %s", dev_name.c_str());
return;
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
LOG_F(
WARNING, "%s: cannot attach non-PCI device %s",
hwc ? hwc->get_name().c_str() : "PCIHost", dev_name.c_str()
);
return NULL;
}
// add device to the machine object
gMachineObj->add_device(dev_name, std::move(dev_obj));
gMachineObj->add_device(dev_name + dev_suffix, std::move(dev_obj));
PCIDevice *dev = dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name + dev_suffix));
// register device with the PCI host
this->pci_register_device(
slot_id, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(dev_name)));
this->pci_register_device(slot_id, dev);
return dev;
}
bool PCIHost::pci_io_read_loop(uint32_t offset, int size, uint32_t &res)
{
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_read(offset, size, &res)) {
return true;
}
}
return false;
}
bool PCIHost::pci_io_write_loop(uint32_t offset, int size, uint32_t value)
{
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_write(offset, value, size)) {
return true;
}
}
return false;
}
uint32_t PCIHost::pci_io_read_broadcast(uint32_t offset, int size)
{
uint32_t res;
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
if (pci_io_read_loop (offset, size, res)) {
return res;
}
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
LOG_F(
ERROR, "%s: Attempt to read from unmapped PCI I/O space @%08x.%c",
hwc ? hwc->get_name().c_str() : "PCIHost", offset,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
return 0;
}
void PCIHost::pci_io_write_broadcast(uint32_t offset, int size, uint32_t value)
{
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
if (pci_io_write_loop(offset, size, value)) {
return;
}
HWComponent *hwc = dynamic_cast<HWComponent*>(this);
LOG_F(
ERROR, "%s: Attempt to write to unmapped PCI I/O space @%08x.%c = %0*x",
hwc ? hwc->get_name().c_str() : "PCIHost", offset,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
size * 2, BYTESWAP_SIZED(value, size)
);
}
PCIDevice *PCIHost::pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num)
{
for (auto& bridge : this->bridge_devs) {
if (bridge->secondary_bus <= bus_num) {
if (bridge->secondary_bus == bus_num) {
if (bridge->dev_map.count(DEV_FUN(dev_num, fun_num))) {
return bridge->dev_map[DEV_FUN(dev_num, fun_num)];
}
}
else if (bridge->subordinate_bus >= bus_num) {
return bridge->pci_find_device(bus_num, dev_num, fun_num);
}
}
}
return NULL;
}

View File

@ -48,7 +48,10 @@ typedef struct AccessDetails {
uint8_t flags;
} AccessDetails;
class PCIDevice; // forward declaration to prevent errors
#define DEV_FUN(dev_num,fun_num) (((dev_num) << 3) | (fun_num))
class PCIDevice;
class PCIBridge;
class PCIHost {
public:
@ -58,18 +61,26 @@ public:
};
~PCIHost() = default;
virtual bool pci_register_device(int dev_num, PCIDevice* dev_instance);
virtual bool pci_register_device(int dev_fun_num, PCIDevice* dev_instance);
virtual bool pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
virtual bool pci_unregister_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj);
virtual void attach_pci_device(std::string& dev_name, int slot_id);
virtual void attach_pci_device(const std::string& dev_name, int slot_id);
PCIDevice *attach_pci_device(const std::string& dev_name, int slot_id, const std::string& dev_suffix);
virtual bool pci_io_read_loop (uint32_t offset, int size, uint32_t &res);
virtual bool pci_io_write_loop(uint32_t offset, int size, uint32_t value);
virtual uint32_t pci_io_read_broadcast (uint32_t offset, int size);
virtual void pci_io_write_broadcast(uint32_t offset, int size, uint32_t value);
virtual PCIDevice *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
protected:
std::unordered_map<int, PCIDevice*> dev_map;
std::vector<PCIDevice*> io_space_devs;
std::vector<PCIBridge*> bridge_devs;
};
// Helpers for data conversion in the PCI Configuration space.

View File

@ -48,7 +48,7 @@ MPC106::MPC106() : MemCtrlBase(), PCIDevice("Grackle"), PCIHost()
this->status = 0x80;
// assign PCI device number zero to myself
this->pci_register_device(0, this);
this->pci_register_device(DEV_FUN(0,0), this);
// add PCI/ISA I/O space, 64K for now
add_mmio_region(0xFE000000, 0x10000, this);
@ -62,7 +62,7 @@ int MPC106::device_postinit()
std::string pci_dev_name;
static const std::map<std::string, int> pci_slots = {
{"pci_A1", 0xD}, {"pci_B1", 0xE}, {"pci_C1", 0xF}
{"pci_A1", DEV_FUN(0xD,0)}, {"pci_B1", DEV_FUN(0xE,0)}, {"pci_C1", DEV_FUN(0xF,0)}
};
for (auto& slot : pci_slots) {
@ -75,105 +75,87 @@ int MPC106::device_postinit()
}
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
uint32_t result;
if (rgn_start == 0xFE000000) {
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_read(offset, size, &result)) {
return result;
}
}
LOG_F(ERROR, "Attempt to read from unmapped PCI I/O space, offset=0x%X", offset);
// FIXME: add machine check exception (DEFAULT CATCH!, code=FFF00200)
} else {
if (offset >= 0x200000) {
if (this->config_addr & 0x80) // process only if bit E (enable) is set
return pci_read(offset, size);
} else {
return pci_io_read_broadcast(offset, size);
}
if (offset < 0x200000) {
return this->config_addr;
}
if (this->config_addr & 0x80) { // process only if bit E (enable) is set
return pci_read(offset, size);
}
return 0;
}
void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
if (rgn_start == 0xFE000000) {
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
for (auto& dev : this->io_space_devs) {
if (dev->pci_io_write(offset, value, size)) {
pci_io_write_broadcast(offset, size, value);
return;
}
}
LOG_F(ERROR, "Attempt to write to unmapped PCI I/O space, offset=0x%X", offset);
} else {
if (offset < 0x200000) {
this->config_addr = value;
} else {
if (this->config_addr & 0x80) // process only if bit E (enable) is set
return;
}
if (this->config_addr & 0x80) { // process only if bit E (enable) is set
return pci_write(offset, value, size);
}
}
}
uint32_t MPC106::pci_read(uint32_t offset, uint32_t size) {
int bus_num = (this->config_addr >> 8) & 0xFF;
int dev_num = (this->config_addr >> 19) & 0x1F;
int fun_num = (this->config_addr >> 16) & 0x07;
int reg_offs = (this->config_addr >> 24) & 0xFC;
if (bus_num) {
LOG_F(ERROR, "%s: read attempt from non-local PCI bus, %02x:%02x.%x @%02x",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3));
return 0xFFFFFFFFUL; // PCI spec §6.1
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
PCIDevice *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_READ;
if (device) {
return pci_conv_rd_data(device->pci_cfg_read(reg_offs, details), details);
}
if (this->dev_map.count(dev_num)) {
AccessDetails details;
details.offset = offset & 3;
details.size = size;
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_READ;
uint32_t result = this->dev_map[dev_num]->pci_cfg_read(reg_offs, details);
return pci_conv_rd_data(result, details);
} else {
LOG_F(ERROR, "%s: read attempt from non-existing PCI device ??:%02x.%x @%02x",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3));
}
LOG_READ_NON_EXISTENT_PCI_DEVICE();
return 0xFFFFFFFFUL; // PCI spec §6.1
}
void MPC106::pci_write(uint32_t offset, uint32_t value, uint32_t size) {
int bus_num = (this->config_addr >> 8) & 0xFF;
int dev_num = (this->config_addr >> 19) & 0x1F;
int fun_num = (this->config_addr >> 16) & 0x07;
int reg_offs = (this->config_addr >> 24) & 0xFC;
if (bus_num) {
LOG_F(ERROR, "%s: write attempt to non-local PCI bus, %02x:%02x.%x @%02x",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3));
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
AccessDetails details;
PCIDevice *device;
cfg_setup(offset, size, bus_num, dev_num, fun_num, reg_offs, details, device);
details.flags |= PCI_CONFIG_WRITE;
if (device) {
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
device->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
return;
}
// otherwise perform necessary data transformations -> slow path
uint32_t old_val = details.size == 4 ? 0 : device->pci_cfg_read(reg_offs, details);
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
device->pci_cfg_write(reg_offs, new_val, details);
return;
}
LOG_WRITE_NON_EXISTENT_PCI_DEVICE();
}
if (this->dev_map.count(dev_num)) {
AccessDetails details;
details.offset = offset & 3;
details.size = size;
details.flags = PCI_CONFIG_TYPE_0 | PCI_CONFIG_WRITE;
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)
{
device = NULL;
details.size = size;
details.offset = offset & 3;
if (size == 4 && !details.offset) { // aligned DWORD writes -> fast path
this->dev_map[dev_num]->pci_cfg_write(reg_offs, BYTESWAP_32(value), details);
} else { // otherwise perform necessary data transformations -> slow path
uint32_t old_val = this->dev_map[dev_num]->pci_cfg_read(reg_offs, details);
uint32_t new_val = pci_conv_wr_data(old_val, value, details);
this->dev_map[dev_num]->pci_cfg_write(reg_offs, new_val, details);
bus_num = (this->config_addr >> 8) & 0xFF;
dev_num = (this->config_addr >> 19) & 0x1F;
fun_num = (this->config_addr >> 16) & 0x07;
reg_offs = (this->config_addr >> 24) & 0xFC;
if (bus_num) {
details.flags = PCI_CONFIG_TYPE_1;
device = pci_find_device(bus_num, dev_num, fun_num);
}
else {
details.flags = PCI_CONFIG_TYPE_0;
if (this->dev_map.count(DEV_FUN(dev_num, fun_num))) {
device = this->dev_map[DEV_FUN(dev_num, fun_num)];
}
} else {
LOG_F(ERROR, "%s: write attempt to non-existing PCI device ??:%02x.%x @%02x",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3));
}
}

View File

@ -92,13 +92,11 @@ protected:
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
bool supports_io_space(void) {
return true;
};
void setup_ram(void);
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);
uint32_t config_addr;
uint16_t pmcr1 = 0; // power management config 1

View File

@ -348,17 +348,13 @@ void ATIRage::write_reg(uint32_t offset, uint32_t value, uint32_t size)
}
bool ATIRage::io_access_allowed(uint32_t offset) {
if (!(this->command & 1)) {
if (offset >= this->io_base && offset < (this->io_base + 0x100)) {
if (this->command & 1) {
return true;
}
LOG_F(WARNING, "ATI I/O space disabled in the command reg");
return false;
}
if (offset < this->io_base || offset > (this->io_base + 0x100)) {
LOG_F(WARNING, "Rage: I/O out of range, base=0x%X, offset=0x%X", io_base, offset);
return false;
}
return true;
return false;
}

View File

@ -60,10 +60,6 @@ public:
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
/* PCI device methods */
bool supports_io_space(void) {
return true;
};
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);

View File

@ -44,7 +44,7 @@ int initialize_catalyst(std::string& id)
// add the GrandCentral I/O controller
pci_host->pci_register_device(
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
// get (raw) pointer to the memory controller
platinum_obj = dynamic_cast<PlatinumCtrl*>(gMachineObj->get_comp_by_name("Platinum"));

View File

@ -53,7 +53,7 @@ int initialize_gazelle(std::string& id)
// register O'Hare I/O controller with the main PCI bus
pci_host->pci_register_device(
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("OHare")));
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("OHare")));
PsxCtrl* psx_obj = dynamic_cast<PsxCtrl*>(gMachineObj->get_comp_by_name("Psx"));

View File

@ -124,9 +124,9 @@ int initialize_gossamer(std::string& id)
// add pci devices
grackle_obj->pci_register_device(
16, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
grackle_obj->pci_register_device(
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(id == "pmg3twr" ? "AtiRagePro" : "AtiRageGT")));
DEV_FUN(0x12,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(id == "pmg3twr" ? "AtiRagePro" : "AtiRageGT")));
// add Athens clock generator device and register it with the I2C host
gMachineObj->add_device("Athens", std::unique_ptr<AthensClocks>(new AthensClocks(0x28)));

View File

@ -44,14 +44,14 @@ int initialize_tnt(std::string& id)
// connect GrandCentral I/O controller to the PCI1 bus
pci_host->pci_register_device(
32, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
DEV_FUN(0x10,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("GrandCentral")));
// get video PCI controller object
PCIHost *vci_host = dynamic_cast<PCIHost*>(gMachineObj->get_comp_by_name("Chaos"));
// connect built-in video device to the VCI bus
vci_host->pci_register_device(
1, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ControlVideo")));
DEV_FUN(0x0B,0), dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ControlVideo")));
// get (raw) pointer to the memory controller
memctrl_obj = dynamic_cast<HammerheadCtrl*>(gMachineObj->get_comp_by_name("Hammerhead"));

View File

@ -27,7 +27,7 @@ It also spans for 0x7F000000 bytes starting from 0x80000000.
The dingusppc CLI can add known PCI devices to a PCI slot (A1, B1, C1).
Only the following device numbers are probed by Open Firmware:
Only the following device numbers connected to grackle are probed by Open Firmware:
- @0 used by pci [grackle]
- @c slot PERCH interrupt 0x1c
@ -37,6 +37,8 @@ Only the following device numbers are probed by Open Firmware:
- @10 used by mac-io [heathrow] which includes many devices with different interrupts
- @12 slot F1 interrupt 0x16 used by AtiRageGT or AtiRagePro
With minor additions to source code, dingusppc can add a known PCI device to any device number between @1 and @1f except for @10 and @12.
With minor additions to source code, dingusppc can add a known PCI device to any device number between @1 and @1f except for @10 and @12. A nvramrc patch can make Open Firmware probe the other device numbers. An OS might be able to probe these other device numbers even if they are not probed by Open Firmware.
A nvramrc patch can make Open Firmware probe the other device numbers. An OS might be able to probe these other devices numbers even if they are not probed by Open Firmware.
A better approach may be to attach a PCI bridge to one of the supported slots. Then additional devices can be attached to the PCI bridge. For a PCI bridge, Open Firmware will probe device numbers between @0 and @f. Some nvramrc patches could maybe make Open Firmware probe the PCI bridge devices up to device number @1f. PCI bridges can be nested. In any case, each device number can have up to 8 functions numbered from 0 to 7.
The dingusppc CLI doesn't yet support adding devices to PCI bridges or adding function numbers 1 to 7.