All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kvm-userspace: Load PCI option ROMs
@ 2008-12-11 10:19 Liu, Kechao
  2008-12-23 17:15 ` Avi Kivity
  0 siblings, 1 reply; 2+ messages in thread
From: Liu, Kechao @ 2008-12-11 10:19 UTC (permalink / raw)
  To: kvm; +Cc: Shan, Haitao, Han, Weidong, Yang, Sheng

[-- 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


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] kvm-userspace: Load PCI option ROMs
  2008-12-11 10:19 [PATCH] kvm-userspace: Load PCI option ROMs Liu, Kechao
@ 2008-12-23 17:15 ` Avi Kivity
  0 siblings, 0 replies; 2+ messages in thread
From: Avi Kivity @ 2008-12-23 17:15 UTC (permalink / raw)
  To: Liu, Kechao; +Cc: kvm, Shan, Haitao, Han, Weidong, Yang, Sheng

Liu, Kechao wrote:
> Load assigned devices' PCI option ROMs to the RAM of
> guest OS. And pass the corresponding devfns to BIOS.
>
>   

Sorry about the late review.

> +
> +    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;
>   

phys_ram_base is dangerous.  It works in this case, but we'd like to 
avoid it.  Please use cpu_physical_memory_write_rom().

> +
> +    cpu_register_physical_memory(0xd0000 + offset, size, addr);
>   

Best to register this as a rom, though kvm will ignore it (we don't 
implement read-only memory at this time).

> +
> +/*
> + * 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);
>   

system()?!  Please just write to the file.

> +
> +        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);
> +
>   

Missing error checking.

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

-- 
error compiling committee.c: too many arguments to function


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2008-12-23 17:15 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-12-11 10:19 [PATCH] kvm-userspace: Load PCI option ROMs Liu, Kechao
2008-12-23 17:15 ` Avi Kivity

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.