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