All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Liu, Kechao" <kechao.liu@intel.com>
To: "kvm@vger.kernel.org" <kvm@vger.kernel.org>
Cc: "Shan, Haitao" <haitao.shan@intel.com>,
	"Han, Weidong" <weidong.han@intel.com>,
	"Yang, Sheng" <sheng.yang@intel.com>
Subject: [PATCH] kvm-userspace: Load PCI option ROMs
Date: Thu, 11 Dec 2008 18:19:07 +0800	[thread overview]
Message-ID: <E88DD564E9DC5446A76B2B47C3BCCA15432C049A@pdsmsx503.ccr.corp.intel.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 7985 bytes --]

Load assigned devices' PCI option ROMs to the RAM of
guest OS. And pass the corresponding devfns to BIOS.

Signed-off-by: Kechao Liu <kechao.liu@intel.com>
---
 bios/rombios.c              |   20 +++++-
 qemu/hw/device-assignment.c |  140 +++++++++++++++++++++++++++++++++++++++++++
 qemu/hw/device-assignment.h |    1 +
 qemu/hw/pc.c                |    8 ++-
 4 files changed, 163 insertions(+), 6 deletions(-)

diff --git a/bios/rombios.c b/bios/rombios.c
index 9a1cdd6..6d63568 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10216,18 +10216,30 @@ rom_scan_loop:
   add  al, #0x04
 block_count_rounded:
 
-  xor  bx, bx   ;; Restore DS back to 0000:
-  mov  ds, bx
   push ax       ;; Save AX
   push di       ;; Save DI
   ;; Push addr of ROM entry point
   push cx       ;; Push seg
   push #0x0003  ;; Push offset
 
+  ;; Get the BDF into ax before invoking the option ROM
+  mov  bl, [2]
+  mov  al, bl
+  shr  al, #7
+  cmp  al, #1
+  jne  fetch_bdf
+  mov  ax, ds ;; Increment the DS since rom size larger than an segment
+  add  ax, #0x1000
+  mov  ds, ax
+fetch_bdf:
+  shl  bx, #9
+  xor  ax, ax
+  mov  al, [bx]
+
   ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
   ;; That should stop it grabbing INT 19h; we will use its BEV instead.
-  mov  ax, #0xf000
-  mov  es, ax
+  mov  bx, #0xf000
+  mov  es, bx
   lea  di, pnp_string
 
   mov  bp, sp   ;; Call ROM init routine using seg:off on stack
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..e53dda4 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -678,3 +678,143 @@ void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices)
         }
     }
 }
+
+/* Option ROM header */
+struct option_rom_header {
+    uint8_t signature[2];
+    uint8_t rom_size;
+    uint32_t entry_point;
+    uint8_t reserved[17];
+    uint16_t pci_header_offset;
+    uint16_t expansion_header_offset;
+} __attribute__ ((packed));
+
+/* Option ROM PCI data structure */
+struct option_rom_pci_header {
+    uint8_t signature[4];
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint16_t vital_product_data_offset;
+    uint16_t structure_length;
+    uint8_t structure_revision;
+    uint8_t class_code[3];
+    uint16_t image_length;
+    uint16_t image_revision;
+    uint8_t code_type;
+    uint8_t indicator;
+    uint16_t reserved;
+} __attribute__ ((packed));
+
+/*
+ * Scan the list of Option ROMs at roms. If a suitable Option ROM is found,
+ * allocate a ram space and copy it there. Then return its size aligned to 
+ * both 2KB and target page size. 
+ */
+#define OPTION_ROM_ALIGN(x) (((x) + 2047) & ~2047)
+static int scan_option_rom(uint8_t devfn, void *roms, ram_addr_t offset)
+{
+    int i, size;
+    uint8_t csum;
+    ram_addr_t addr, phys_addr;
+    struct option_rom_header *rom;
+    struct option_rom_pci_header *pcih;
+
+    rom = roms;
+
+    for ( ; ; ) {
+        /* Invalid signature means we're out of option ROMs. */
+        if (strncmp((char *)rom->signature, "\x55\xaa", 2) ||
+             (rom->rom_size == 0))
+            break;
+
+        /* Invalid checksum means we're out of option ROMs. */
+        csum = 0;
+        for (i = 0; i < (rom->rom_size * 512); i++)
+            csum += ((uint8_t *)rom)[i];
+        if (csum != 0) 
+            break;
+
+        /* Check the PCI header (if any) for a match. */
+        pcih = (struct option_rom_pci_header *)
+                ((char *)rom + rom->pci_header_offset);
+        if ((rom->pci_header_offset != 0) &&
+             !strncmp((char *)pcih->signature, "PCIR", 4))
+            goto found;
+
+        rom = (struct option_rom_header *)((char *)rom + rom->rom_size * 512);
+    }
+
+    return 0;
+
+ found:
+    /* The size should be both 2K-aligned and page-aligned */
+    size = (TARGET_PAGE_SIZE < 0x800)
+            ? OPTION_ROM_ALIGN(rom->rom_size * 512 + 1)
+            : TARGET_PAGE_ALIGN(rom->rom_size * 512 + 1);
+
+    /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */
+    if ((offset + size) > 0x10000u) {
+        fprintf(stderr, "Option ROM size %x exceeds available space\n",
+                rom->rom_size * 512);
+        return 0;
+    }
+
+    addr = qemu_ram_alloc(size);
+    phys_addr = addr + phys_ram_base;
+
+    /* Write ROM data and devfn to phys_addr */
+    memcpy((void *)phys_addr, rom, rom->rom_size * 512);
+    *(uint8_t *)(phys_addr + rom->rom_size * 512) = devfn;
+
+    cpu_register_physical_memory(0xd0000 + offset, size, addr);
+    
+    return size; 
+}
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, 
+ * and then load the corresponding ROM data to RAM.
+ */
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset)
+{
+    ram_addr_t offset = rom_base_offset;
+    AssignedDevInfo *adev;
+
+    LIST_FOREACH(adev, &adev_head, next) {
+        int size;
+        void *buf;
+        FILE *fp;
+        char rom_file[64]; 
+        char cmd[64];
+
+        snprintf(rom_file, sizeof(rom_file), 
+                 "/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom", 
+                 adev->bus, adev->dev, adev->func);
+   
+        if (access(rom_file, F_OK))
+            continue;
+
+        /* Write something to the ROM file to enable it */
+        snprintf(cmd, sizeof(cmd), "echo 1 > %s", rom_file);
+        system(cmd);
+
+        fp = fopen(rom_file, "rb");
+        if (fp == NULL)
+            continue;
+
+        /* Read the data of the ROM file to the buffer */
+        fseek(fp, 0, SEEK_END);
+        size = ftell(fp);
+        fseek(fp, 0, SEEK_SET);
+        buf = malloc(size);
+        fread(buf, size, 1, fp);
+
+        /* Scan the buffer for suitable ROMs and increase the offset */
+        offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset);
+        
+        free(buf);
+        fclose(fp);
+    }
+
+    return offset;
+}
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index c8c47d3..a565948 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -98,6 +98,7 @@ void free_assigned_device(AssignedDevInfo *adev);
 PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus);
 AssignedDevInfo *add_assigned_device(const char *arg);
 void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices);
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset);
 
 #define MAX_DEV_ASSIGN_CMDLINE 8
 
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 3cf5a73..9b133da 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -809,6 +809,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
     ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
     ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
     int bios_size, isa_bios_size, vga_bios_size, opt_rom_offset;
+    int pci_option_rom_offset;
     PCIBus *pci_bus;
     int piix3_devfn = -1;
     CPUState *env;
@@ -971,6 +972,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
             option_rom_setup_reset(0xd0000 + offset, size);
             offset += size;
         }
+        pci_option_rom_offset = offset;
     }
 
     /* map all the bios at the top of memory */
@@ -1187,8 +1189,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
         virtio_balloon_init(pci_bus);
 
 #ifdef USE_KVM_DEVICE_ASSIGNMENT
-    if (kvm_enabled())
-	add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+    if (kvm_enabled()) {
+        add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+        assigned_dev_load_option_roms(pci_option_rom_offset);
+    }
 #endif /* USE_KVM_DEVICE_ASSIGNMENT */
 }
 
-- 
1.6.0

[-- Attachment #2: 0001-kvm-userspace-Load-PCI-option-ROMs.patch --]
[-- Type: application/octet-stream, Size: 7944 bytes --]

From 93a5f3c2e27df6aed65359cd7f4f44cf065d79bb Mon Sep 17 00:00:00 2001
From: Kechao Liu <kechao.liu@intel.com>
Date: Thu, 11 Dec 2008 17:49:40 +0800
Subject: [PATCH] kvm-userspace: Load PCI option ROMs

Load assigned devices' PCI option ROMs to the RAM of
guest OS. And pass the corresponding devfns to BIOS.

Signed-off-by: Kechao Liu <kechao.liu@intel.com>
---
 bios/rombios.c              |   20 +++++-
 qemu/hw/device-assignment.c |  140 +++++++++++++++++++++++++++++++++++++++++++
 qemu/hw/device-assignment.h |    1 +
 qemu/hw/pc.c                |    8 ++-
 4 files changed, 163 insertions(+), 6 deletions(-)

diff --git a/bios/rombios.c b/bios/rombios.c
index 9a1cdd6..6d63568 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10216,18 +10216,30 @@ rom_scan_loop:
   add  al, #0x04
 block_count_rounded:
 
-  xor  bx, bx   ;; Restore DS back to 0000:
-  mov  ds, bx
   push ax       ;; Save AX
   push di       ;; Save DI
   ;; Push addr of ROM entry point
   push cx       ;; Push seg
   push #0x0003  ;; Push offset
 
+  ;; Get the BDF into ax before invoking the option ROM
+  mov  bl, [2]
+  mov  al, bl
+  shr  al, #7
+  cmp  al, #1
+  jne  fetch_bdf
+  mov  ax, ds ;; Increment the DS since rom size larger than an segment
+  add  ax, #0x1000
+  mov  ds, ax
+fetch_bdf:
+  shl  bx, #9
+  xor  ax, ax
+  mov  al, [bx]
+
   ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
   ;; That should stop it grabbing INT 19h; we will use its BEV instead.
-  mov  ax, #0xf000
-  mov  es, ax
+  mov  bx, #0xf000
+  mov  es, bx
   lea  di, pnp_string
 
   mov  bp, sp   ;; Call ROM init routine using seg:off on stack
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..e53dda4 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -678,3 +678,143 @@ void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices)
         }
     }
 }
+
+/* Option ROM header */
+struct option_rom_header {
+    uint8_t signature[2];
+    uint8_t rom_size;
+    uint32_t entry_point;
+    uint8_t reserved[17];
+    uint16_t pci_header_offset;
+    uint16_t expansion_header_offset;
+} __attribute__ ((packed));
+
+/* Option ROM PCI data structure */
+struct option_rom_pci_header {
+    uint8_t signature[4];
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint16_t vital_product_data_offset;
+    uint16_t structure_length;
+    uint8_t structure_revision;
+    uint8_t class_code[3];
+    uint16_t image_length;
+    uint16_t image_revision;
+    uint8_t code_type;
+    uint8_t indicator;
+    uint16_t reserved;
+} __attribute__ ((packed));
+
+/*
+ * Scan the list of Option ROMs at roms. If a suitable Option ROM is found,
+ * allocate a ram space and copy it there. Then return its size aligned to 
+ * both 2KB and target page size. 
+ */
+#define OPTION_ROM_ALIGN(x) (((x) + 2047) & ~2047)
+static int scan_option_rom(uint8_t devfn, void *roms, ram_addr_t offset)
+{
+    int i, size;
+    uint8_t csum;
+    ram_addr_t addr, phys_addr;
+    struct option_rom_header *rom;
+    struct option_rom_pci_header *pcih;
+
+    rom = roms;
+
+    for ( ; ; ) {
+        /* Invalid signature means we're out of option ROMs. */
+        if (strncmp((char *)rom->signature, "\x55\xaa", 2) ||
+             (rom->rom_size == 0))
+            break;
+
+        /* Invalid checksum means we're out of option ROMs. */
+        csum = 0;
+        for (i = 0; i < (rom->rom_size * 512); i++)
+            csum += ((uint8_t *)rom)[i];
+        if (csum != 0) 
+            break;
+
+        /* Check the PCI header (if any) for a match. */
+        pcih = (struct option_rom_pci_header *)
+                ((char *)rom + rom->pci_header_offset);
+        if ((rom->pci_header_offset != 0) &&
+             !strncmp((char *)pcih->signature, "PCIR", 4))
+            goto found;
+
+        rom = (struct option_rom_header *)((char *)rom + rom->rom_size * 512);
+    }
+
+    return 0;
+
+ found:
+    /* The size should be both 2K-aligned and page-aligned */
+    size = (TARGET_PAGE_SIZE < 0x800)
+            ? OPTION_ROM_ALIGN(rom->rom_size * 512 + 1)
+            : TARGET_PAGE_ALIGN(rom->rom_size * 512 + 1);
+
+    /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */
+    if ((offset + size) > 0x10000u) {
+        fprintf(stderr, "Option ROM size %x exceeds available space\n",
+                rom->rom_size * 512);
+        return 0;
+    }
+
+    addr = qemu_ram_alloc(size);
+    phys_addr = addr + phys_ram_base;
+
+    /* Write ROM data and devfn to phys_addr */
+    memcpy((void *)phys_addr, rom, rom->rom_size * 512);
+    *(uint8_t *)(phys_addr + rom->rom_size * 512) = devfn;
+
+    cpu_register_physical_memory(0xd0000 + offset, size, addr);
+    
+    return size; 
+}
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, 
+ * and then load the corresponding ROM data to RAM.
+ */
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset)
+{
+    ram_addr_t offset = rom_base_offset;
+    AssignedDevInfo *adev;
+
+    LIST_FOREACH(adev, &adev_head, next) {
+        int size;
+        void *buf;
+        FILE *fp;
+        char rom_file[64]; 
+        char cmd[64];
+
+        snprintf(rom_file, sizeof(rom_file), 
+                 "/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom", 
+                 adev->bus, adev->dev, adev->func);
+   
+        if (access(rom_file, F_OK))
+            continue;
+
+        /* Write something to the ROM file to enable it */
+        snprintf(cmd, sizeof(cmd), "echo 1 > %s", rom_file);
+        system(cmd);
+
+        fp = fopen(rom_file, "rb");
+        if (fp == NULL)
+            continue;
+
+        /* Read the data of the ROM file to the buffer */
+        fseek(fp, 0, SEEK_END);
+        size = ftell(fp);
+        fseek(fp, 0, SEEK_SET);
+        buf = malloc(size);
+        fread(buf, size, 1, fp);
+
+        /* Scan the buffer for suitable ROMs and increase the offset */
+        offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset);
+        
+        free(buf);
+        fclose(fp);
+    }
+
+    return offset;
+}
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index c8c47d3..a565948 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -98,6 +98,7 @@ void free_assigned_device(AssignedDevInfo *adev);
 PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus);
 AssignedDevInfo *add_assigned_device(const char *arg);
 void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices);
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset);
 
 #define MAX_DEV_ASSIGN_CMDLINE 8
 
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 3cf5a73..9b133da 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -809,6 +809,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
     ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
     ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
     int bios_size, isa_bios_size, vga_bios_size, opt_rom_offset;
+    int pci_option_rom_offset;
     PCIBus *pci_bus;
     int piix3_devfn = -1;
     CPUState *env;
@@ -971,6 +972,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
             option_rom_setup_reset(0xd0000 + offset, size);
             offset += size;
         }
+        pci_option_rom_offset = offset;
     }
 
     /* map all the bios at the top of memory */
@@ -1187,8 +1189,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
         virtio_balloon_init(pci_bus);
 
 #ifdef USE_KVM_DEVICE_ASSIGNMENT
-    if (kvm_enabled())
-	add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+    if (kvm_enabled()) {
+        add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+        assigned_dev_load_option_roms(pci_option_rom_offset);
+    }
 #endif /* USE_KVM_DEVICE_ASSIGNMENT */
 }
 
-- 
1.6.0


             reply	other threads:[~2008-12-11 10:19 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-12-11 10:19 Liu, Kechao [this message]
2008-12-23 17:15 ` [PATCH] kvm-userspace: Load PCI option ROMs Avi Kivity

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=E88DD564E9DC5446A76B2B47C3BCCA15432C049A@pdsmsx503.ccr.corp.intel.com \
    --to=kechao.liu@intel.com \
    --cc=haitao.shan@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=sheng.yang@intel.com \
    --cc=weidong.han@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.