qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 1/2] pci: Automatically patch PCI vendor id and


From: Anthony Liguori
Subject: Re: [Qemu-devel] [PATCH 1/2] pci: Automatically patch PCI vendor id and device id in PCI ROM
Date: Mon, 18 Oct 2010 13:53:50 -0500
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.12) Gecko/20100915 Lightning/1.0b1 Thunderbird/3.0.8

On 10/18/2010 01:44 PM, Anthony Liguori wrote:
On 10/18/2010 12:55 PM, Stefan Weil wrote:
PCI devices with different vendor or device ids sometimes share
the same rom code. Only the ids and the checksum
differs in a boot rom for such devices.

The i825xx ethernet controller family is a typical example
which is implemented in hw/eepro100.c. It uses at least
3 different device ids, so normally 3 boot roms would be needed.

By automatically patching vendor id and device id (and the checksum)
in qemu, all emulated family members can share the same boot rom.

VGA bios roms are another example with different vendor and device ids.

v2:

* Patch also the vendor id (and remove the sanity check for vendor id).

Cc: Gerd Hoffmann<address@hidden>
Cc: Markus Armbruster<address@hidden>
Cc: Michael S. Tsirkin<address@hidden>
Signed-off-by: Stefan Weil<address@hidden>

I get very nervous about patching a ROM. Who's to say that the ROM doesn't somehow depend on the contents of its header? Maybe it has an internal CRC built into it or something like that.

As part of PMM, ROMs typically reduce their size by decompressing and removing code or something of that nature and then rewrite themselves in scratch RAM. The BIOS then copies the resulting ROM (using the ROM size in the base header as an indication of how much to copy) into the option ROM space.

So the likelihood of depending on the contents of the header seems non-trivial to me.

Regards,

Anthony Liguori

Regards,

Anthony Liguori

---
hw/pci.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 58 insertions(+), 0 deletions(-)

diff --git a/hw/pci.c b/hw/pci.c
index 1280d4d..139eb24 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1797,6 +1797,62 @@ static void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, p
      cpu_register_physical_memory(addr, size, pdev->rom_offset);
  }

+/* Patch the PCI vendor and device ids in a PCI rom image if necessary.
+ This is needed for an option rom which is used for more than one device. */
+static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size)
+{
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint16_t rom_vendor_id;
+    uint16_t rom_device_id;
+    uint16_t rom_magic;
+    uint16_t pcir_offset;
+    uint8_t checksum;
+
+    /* Words in rom data are little endian (like in PCI configuration),
+ so they can be read / written with pci_get_word / pci_set_word. */
+
+    /* Only a valid rom will be patched. */
+    rom_magic = pci_get_word(ptr);
+    if (rom_magic != 0xaa55) {
+        PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic);
+        return;
+    }
+    pcir_offset = pci_get_word(ptr + 0x18);
+ if (pcir_offset + 8>= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { + PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset);
+        return;
+    }
+
+    vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
+    device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
+    rom_vendor_id = pci_get_word(ptr + pcir_offset + 4);
+    rom_device_id = pci_get_word(ptr + pcir_offset + 6);
+
+    PCI_DPRINTF("ROM id %04x%04x / PCI id %04x%04x\n",
+                vendor_id, device_id, rom_vendor_id, rom_device_id);
+
+    checksum = ptr[6];
+
+    if (vendor_id != rom_vendor_id) {
+ /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id>> 8);
+        checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id>>  8);
+        PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum);
+        ptr[6] = checksum;
+        pci_set_word(ptr + pcir_offset + 4, vendor_id);
+    }
+
+    if (device_id != rom_device_id) {
+ /* Patch device id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id>> 8);
+        checksum -= (uint8_t)device_id + (uint8_t)(device_id>>  8);
+        PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum);
+        ptr[6] = checksum;
+        pci_set_word(ptr + pcir_offset + 6, device_id);
+    }
+}
+
  /* Add an option rom for the device */
  static int pci_add_option_rom(PCIDevice *pdev)
  {
@@ -1849,6 +1905,8 @@ static int pci_add_option_rom(PCIDevice *pdev)
      load_image(path, ptr);
      qemu_free(path);

+    pci_patch_ids(pdev, ptr, size);
+
      pci_register_bar(pdev, PCI_ROM_SLOT, size,
                       0, pci_map_option_rom);







reply via email to

[Prev in Thread] Current Thread [Next in Thread]