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 */