pcidevice: loading of expansion ROMs from files.

This commit is contained in:
Maxim Poliakovski 2022-04-13 23:01:27 +02:00
parent 4bba61a920
commit b78f17c161
2 changed files with 91 additions and 3 deletions

View File

@ -26,6 +26,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <memaccess.h>
#include <cinttypes>
#include <fstream>
#include <string>
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<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(), 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);
}
}

View File

@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
/** PCI configuration space registers offsets */
@ -91,6 +92,8 @@ public:
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;
};
@ -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<uint8_t[]> exp_rom_data;
};
#endif /* PCI_DEVICE_H */