diff --git a/devices/common/pci/pcidevice.cpp b/devices/common/pci/pcidevice.cpp
index 8480354..c92ec2a 100644
--- a/devices/common/pci/pcidevice.cpp
+++ b/devices/common/pci/pcidevice.cpp
@@ -26,6 +26,8 @@ along with this program. If not, see .
#include
#include
+#include
+#include
PCIDevice::PCIDevice(std::string name)
{
@@ -132,7 +134,13 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
if (data == 0xFFFFF800UL) {
this->exp_rom_bar = this->exp_bar_cfg;
} else {
- this->exp_rom_bar = (data & 0xFFFFF800UL) | (this->exp_bar_cfg & 1);
+ this->exp_rom_bar = (data & 0xFFFFF801UL);
+ if (this->exp_rom_bar & 1) {
+ this->map_exp_rom_mem(this->exp_rom_bar & 0xFFFFF800UL);
+ } else {
+ LOG_F(WARNING, "%s: unmapping of expansion ROM not implemented yet",
+ this->pci_name.c_str());
+ }
}
break;
case PCI_CFG_DWORD_15:
@@ -144,6 +152,70 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
}
}
+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);
+ this->exp_rom_size = img_file.tellg();
+
+ // verify PCI struct offset
+ uint32_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 > this->exp_rom_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");
+ }
+
+ // ROM image ok - go ahead and load it
+ this->exp_rom_data = std::unique_ptr (new uint8_t[this->exp_rom_size]);
+ img_file.seekg(0, std::ios::beg);
+ img_file.read((char *)this->exp_rom_data.get(), this->exp_rom_size);
+
+ // align ROM image size on a 2KB boundary and initialize ROM config
+ this->exp_rom_size = (this->exp_rom_size + 0x7FFU) & 0xFFFFF800UL;
+ this->exp_bar_cfg = ~(this->exp_rom_size - 1);
+ }
+ catch (const std::exception& exc) {
+ LOG_F(ERROR, "PCIDevice: %s", exc.what());
+ result = -1;
+ }
+
+ img_file.close();
+
+ return result;
+}
+
void PCIDevice::do_bar_sizing(int bar_num)
{
this->bars[bar_num] = this->bars_cfg[bar_num];
@@ -162,3 +234,10 @@ void PCIDevice::set_bar_value(int bar_num, uint32_t value)
}
this->pci_notify_bar_change(bar_num);
}
+
+void PCIDevice::map_exp_rom_mem(uint32_t rom_addr)
+{
+ if (!this->exp_rom_addr || this->exp_rom_addr != rom_addr) {
+ this->host_instance->pci_register_mmio_region(rom_addr, 0x10000, this);
+ }
+}
diff --git a/devices/common/pci/pcidevice.h b/devices/common/pci/pcidevice.h
index b490283..f6c03de 100644
--- a/devices/common/pci/pcidevice.h
+++ b/devices/common/pci/pcidevice.h
@@ -27,6 +27,7 @@ along with this program. If not, see .
#include
#include
+#include
#include
/** PCI configuration space registers offsets */
@@ -91,6 +92,8 @@ public:
std::function pci_notify_bar_change;
+ int attach_exp_rom_image(const std::string img_path);
+
virtual void set_host(PCIHost* host_instance) {
this->host_instance = host_instance;
};
@@ -98,6 +101,7 @@ public:
protected:
void do_bar_sizing(int bar_num);
void set_bar_value(int bar_num, uint32_t value);
+ void map_exp_rom_mem(uint32_t rom_addr);
std::string pci_name; // human-readable device name
PCIHost* host_instance; // host bridge instance to call back
@@ -120,8 +124,13 @@ protected:
uint32_t bars[6] = { 0 }; // base address registers
uint32_t bars_cfg[6] = { 0 }; // configuration values for base address registers
- uint32_t exp_rom_bar = 0; // expansion ROM base address
- uint32_t exp_bar_cfg = 0;
+
+ uint32_t exp_bar_cfg = 0; // expansion ROM configuration
+ uint32_t exp_rom_bar = 0; // expansion ROM base address register
+ uint32_t exp_rom_addr = 0; // expansion ROM base address
+ uint32_t exp_rom_size = 0; // expansion ROM size in bytes
+
+ std::unique_ptr exp_rom_data;
};
#endif /* PCI_DEVICE_H */