* [PATCH][v2] kvm-userspace: Load PCI option ROMs
@ 2008-12-29 3:51 Liu, Kechao
2008-12-29 8:28 ` Avi Kivity
0 siblings, 1 reply; 19+ messages in thread
From: Liu, Kechao @ 2008-12-29 3:51 UTC (permalink / raw)
To: avi@redhat.com, kvm@vger.kernel.org; +Cc: Shan, Haitao
[-- Attachment #1: Type: text/plain, Size: 8653 bytes --]
Hi Avi,
Thanks for your comments. I've updated the patch according to them.
Please review it. Thank you.
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 | 155 +++++++++++++++++++++++++++++++++++++++++++
qemu/hw/device-assignment.h | 1 +
qemu/hw/pc.c | 8 ++-
4 files changed, 178 insertions(+), 6 deletions(-)
diff --git a/bios/rombios.c b/bios/rombios.c
index b7a240f..e98d4ff 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10253,18 +10253,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..7f16970 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -678,3 +678,158 @@ 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, total_size;
+ uint8_t csum;
+ ram_addr_t 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;
+
+ size = rom->rom_size * 512;
+ /* Invalid checksum means we're out of option ROMs. */
+ csum = 0;
+ for (i = 0; i < size; 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 + size);
+ }
+
+ return 0;
+
+ found:
+ /* The size should be both 2K-aligned and page-aligned */
+ total_size = (TARGET_PAGE_SIZE < 2048)
+ ? OPTION_ROM_ALIGN(size + 1)
+ : TARGET_PAGE_ALIGN(size + 1);
+
+ /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */
+ if ((offset + total_size) > 0x10000u) {
+ fprintf(stderr, "Option ROM size %x exceeds available space\n", size);
+ return 0;
+ }
+
+ addr = qemu_ram_alloc(total_size);
+ cpu_register_physical_memory(0xd0000 + offset, total_size, addr | IO_MEM_ROM);
+
+ /* Write ROM data and devfn to phys_addr */
+ cpu_physical_memory_write_rom(0xd0000 + offset, rom, size);
+ cpu_physical_memory_write_rom(0xd0000 + offset + size, &devfn, 1);
+
+ return total_size;
+}
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, and then
+ * load the corresponding ROM data to RAM. If an error occurs while loading an
+ * option ROM, we just ignore that option ROM and continue with the next one.
+ */
+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, len;
+ void *buf;
+ FILE *fp;
+ uint8_t i = 1;
+ char rom_file[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 */
+ fp = fopen(rom_file, "wb");
+ if (fp == NULL)
+ continue;
+ len = fwrite(&i, 1, 1, fp);
+ fclose(fp);
+ if (len != 1)
+ continue;
+
+ /* The file has to be closed and reopened, otherwise it won't work */
+ fp = fopen(rom_file, "rb");
+ if (fp == NULL)
+ continue;
+
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ buf = malloc(size);
+ if (buf == NULL) {
+ fclose(fp);
+ continue;
+ }
+
+ fread(buf, size, 1, fp);
+ if (!feof(fp) || ferror(fp)) {
+ free(buf);
+ fclose(fp);
+ continue;
+ }
+
+ /* 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 2d7e7f0..c470646 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -810,6 +810,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;
@@ -972,6 +973,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 */
@@ -1190,8 +1192,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
Best Regards,
Liu, Kechao
[-- Attachment #2: 0001-kvm-userspace-Load-PCI-option-ROMs.patch --]
[-- Type: application/octet-stream, Size: 8455 bytes --]
From 3c54b381e4bd8874265ec9337cb4c507d4c8fc7a Mon Sep 17 00:00:00 2001
From: Kechao Liu <kechao.liu@intel.com>
Date: Mon, 29 Dec 2008 11:23:22 +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 | 155 +++++++++++++++++++++++++++++++++++++++++++
qemu/hw/device-assignment.h | 1 +
qemu/hw/pc.c | 8 ++-
4 files changed, 178 insertions(+), 6 deletions(-)
diff --git a/bios/rombios.c b/bios/rombios.c
index b7a240f..e98d4ff 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10253,18 +10253,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..7f16970 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -678,3 +678,158 @@ 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, total_size;
+ uint8_t csum;
+ ram_addr_t 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;
+
+ size = rom->rom_size * 512;
+ /* Invalid checksum means we're out of option ROMs. */
+ csum = 0;
+ for (i = 0; i < size; 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 + size);
+ }
+
+ return 0;
+
+ found:
+ /* The size should be both 2K-aligned and page-aligned */
+ total_size = (TARGET_PAGE_SIZE < 2048)
+ ? OPTION_ROM_ALIGN(size + 1)
+ : TARGET_PAGE_ALIGN(size + 1);
+
+ /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */
+ if ((offset + total_size) > 0x10000u) {
+ fprintf(stderr, "Option ROM size %x exceeds available space\n", size);
+ return 0;
+ }
+
+ addr = qemu_ram_alloc(total_size);
+ cpu_register_physical_memory(0xd0000 + offset, total_size, addr | IO_MEM_ROM);
+
+ /* Write ROM data and devfn to phys_addr */
+ cpu_physical_memory_write_rom(0xd0000 + offset, rom, size);
+ cpu_physical_memory_write_rom(0xd0000 + offset + size, &devfn, 1);
+
+ return total_size;
+}
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, and then
+ * load the corresponding ROM data to RAM. If an error occurs while loading an
+ * option ROM, we just ignore that option ROM and continue with the next one.
+ */
+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, len;
+ void *buf;
+ FILE *fp;
+ uint8_t i = 1;
+ char rom_file[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 */
+ fp = fopen(rom_file, "wb");
+ if (fp == NULL)
+ continue;
+ len = fwrite(&i, 1, 1, fp);
+ fclose(fp);
+ if (len != 1)
+ continue;
+
+ /* The file has to be closed and reopened, otherwise it won't work */
+ fp = fopen(rom_file, "rb");
+ if (fp == NULL)
+ continue;
+
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ buf = malloc(size);
+ if (buf == NULL) {
+ fclose(fp);
+ continue;
+ }
+
+ fread(buf, size, 1, fp);
+ if (!feof(fp) || ferror(fp)) {
+ free(buf);
+ fclose(fp);
+ continue;
+ }
+
+ /* 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 2d7e7f0..c470646 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -810,6 +810,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;
@@ -972,6 +973,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 */
@@ -1190,8 +1192,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] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-29 3:51 [PATCH][v2] kvm-userspace: Load PCI option ROMs Liu, Kechao
@ 2008-12-29 8:28 ` Avi Kivity
2008-12-29 9:36 ` Liu, Kechao
0 siblings, 1 reply; 19+ messages in thread
From: Avi Kivity @ 2008-12-29 8:28 UTC (permalink / raw)
To: Liu, Kechao; +Cc: kvm@vger.kernel.org, Shan, Haitao
Liu, Kechao wrote:
> Hi Avi,
>
> Thanks for your comments. I've updated the patch according to them.
> Please review it. Thank you.
>
> Load assigned devices' PCI option ROMs to the RAM of
> guest OS. And pass the corresponding devfns to BIOS.
>
>
Looks good.
> +
> + /* Write ROM data and devfn to phys_addr */
> + cpu_physical_memory_write_rom(0xd0000 + offset, rom, size);
> + cpu_physical_memory_write_rom(0xd0000 + offset + size, &devfn, 1);
> +
How is the last bit performed on real hardware? Obviously the ROM can't
have devfn embedded.
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-29 8:28 ` Avi Kivity
@ 2008-12-29 9:36 ` Liu, Kechao
2008-12-29 9:49 ` Avi Kivity
0 siblings, 1 reply; 19+ messages in thread
From: Liu, Kechao @ 2008-12-29 9:36 UTC (permalink / raw)
To: Avi Kivity; +Cc: kvm@vger.kernel.org, Shan, Haitao
Hi Avi,
>-----Original Message-----
>From: Avi Kivity [mailto:avi@redhat.com]
>Sent: 2008年12月29日 16:29
>To: Liu, Kechao
>Cc: kvm@vger.kernel.org; Shan, Haitao
>Subject: Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
>
>Liu, Kechao wrote:
>> Hi Avi,
>>
>> Thanks for your comments. I've updated the patch according to them.
>> Please review it. Thank you.
>>
>> Load assigned devices' PCI option ROMs to the RAM of
>> guest OS. And pass the corresponding devfns to BIOS.
>>
>>
>
>Looks good.
>
>> +
>> + /* Write ROM data and devfn to phys_addr */
>> + cpu_physical_memory_write_rom(0xd0000 + offset, rom, size);
>> + cpu_physical_memory_write_rom(0xd0000 + offset + size, &devfn, 1);
>> +
>
>How is the last bit performed on real hardware? Obviously the ROM can't
>have devfn embedded.
On a real hardware, BIOS scans PCI devices, loads ROMs and it can get devices' devfns. Here, in an easier way, we load option ROMs in QEMU and thus need to
store and pass the devfns.
>
>--
>I have a truly marvellous patch that fixes the bug which this
>signature is too narrow to contain.
Best Regards,
Liu, Kechao
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-29 9:36 ` Liu, Kechao
@ 2008-12-29 9:49 ` Avi Kivity
2008-12-30 1:01 ` Shan, Haitao
0 siblings, 1 reply; 19+ messages in thread
From: Avi Kivity @ 2008-12-29 9:49 UTC (permalink / raw)
To: Liu, Kechao; +Cc: kvm@vger.kernel.org, Shan, Haitao
Liu, Kechao wrote:
>> How is the last bit performed on real hardware? Obviously the ROM can't
>> have devfn embedded.
>>
>
> On a real hardware, BIOS scans PCI devices, loads ROMs and it can get devices' devfns. Here, in an easier way, we load option ROMs in QEMU and thus need to
> store and pass the devfns.
>
Well, it may make sense to provide the ROMs as virtual PCI BARs, and
have the bios do the work. This way, if some driver relies on remapping
the BAR (graphic cards?), it can still work.
--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-29 9:49 ` Avi Kivity
@ 2008-12-30 1:01 ` Shan, Haitao
2008-12-30 15:51 ` Avi Kivity
0 siblings, 1 reply; 19+ messages in thread
From: Shan, Haitao @ 2008-12-30 1:01 UTC (permalink / raw)
To: 'Avi Kivity', Liu, Kechao; +Cc: 'kvm@vger.kernel.org'
Avi Kivity wrote:
> Liu, Kechao wrote:
>>> How is the last bit performed on real hardware? Obviously the ROM
>>> can't have devfn embedded.
>>>
>>
>> On a real hardware, BIOS scans PCI devices, loads ROMs and it can
>> get devices' devfns. Here, in an easier way, we load option ROMs in
>> QEMU and thus need to store and pass the devfns.
>>
>
> Well, it may make sense to provide the ROMs as virtual PCI BARs, and
> have the bios do the work. This way, if some driver relies on
> remapping the BAR (graphic cards?), it can still work.
I do not quite understand this. Can you elaborate?
Best Regards
Shan Haitao
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-30 1:01 ` Shan, Haitao
@ 2008-12-30 15:51 ` Avi Kivity
2008-12-31 2:06 ` Shan, Haitao
0 siblings, 1 reply; 19+ messages in thread
From: Avi Kivity @ 2008-12-30 15:51 UTC (permalink / raw)
To: Shan, Haitao; +Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Shan, Haitao wrote:
>> Well, it may make sense to provide the ROMs as virtual PCI BARs, and
>> have the bios do the work. This way, if some driver relies on
>> remapping the BAR (graphic cards?), it can still work.
>>
>
> I do not quite understand this. Can you elaborate?
>
Which part?
I propose not to copy the card option rom to the bios. Instead, present
the bios as a virtual PCI BAR. The loop that scans the ROMs can then be
moved to the bios.
The advantage is that if a driver needs something in the ROM (mode
tables for graphics), they will be available.
I don't know if any card actually requires this. Maybe all the
information is in the driver.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-30 15:51 ` Avi Kivity
@ 2008-12-31 2:06 ` Shan, Haitao
2008-12-31 9:28 ` Avi Kivity
0 siblings, 1 reply; 19+ messages in thread
From: Shan, Haitao @ 2008-12-31 2:06 UTC (permalink / raw)
To: 'Avi Kivity'; +Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Hi, Avi,
Option ROM already has its own BAR at 0x30h. I think the devices assignment code now already handles this register.
Can I summary your proposals like the following: In guest BIOS, scan all the pci devices (virtual) for existance of Option ROMs. Copy them to available BIOS space in 0xC0000 - 0xDFFFF. Execute the ROM code at copied location.
I don't understand why this makes differences compared to scanning and copying Option ROMs in QEMU, especially given that the VGA BIOS and etherboot ROM are also copied to BIOS space in QEMU before they execute in rom_scan loop in guest BIOS.
Thanks!
Best Regards
Haitao Shan
Avi Kivity wrote:
> Shan, Haitao wrote:
>>> Well, it may make sense to provide the ROMs as virtual PCI BARs, and
>>> have the bios do the work. This way, if some driver relies on
>>> remapping the BAR (graphic cards?), it can still work.
>>>
>>
>> I do not quite understand this. Can you elaborate?
>>
>
> Which part?
>
> I propose not to copy the card option rom to the bios. Instead,
> present the bios as a virtual PCI BAR. The loop that scans the ROMs
> can then be moved to the bios.
>
> The advantage is that if a driver needs something in the ROM (mode
> tables for graphics), they will be available.
>
> I don't know if any card actually requires this. Maybe all the
> information is in the driver.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-31 2:06 ` Shan, Haitao
@ 2008-12-31 9:28 ` Avi Kivity
2009-01-03 2:29 ` Shan, Haitao
2009-01-04 2:12 ` Shan, Haitao
0 siblings, 2 replies; 19+ messages in thread
From: Avi Kivity @ 2008-12-31 9:28 UTC (permalink / raw)
To: Shan, Haitao; +Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Shan, Haitao wrote:
> Hi, Avi,
>
> Option ROM already has its own BAR at 0x30h. I think the devices assignment code now already handles this register.
>
Okay good.
> Can I summary your proposals like the following: In guest BIOS, scan all the pci devices (virtual) for existance of Option ROMs. Copy them to available BIOS space in 0xC0000 - 0xDFFFF. Execute the ROM code at copied location.
>
Yes.
> I don't understand why this makes differences compared to scanning and copying Option ROMs in QEMU,
If the ROM BAR is already handled (including registering the memory when
the BAR is programmed -- I don't see that in the code), then there is no
big advantage. It's closer to how real hardware works, but that's about it.
> especially given that the VGA BIOS and etherboot ROM are also copied to BIOS space in QEMU before they execute in rom_scan loop in guest BIOS.
>
The VGA BIOS is typically present on the motherboard itself, at least on
some configurations. You're right about etherboot.
I'll apply the patch. Can you take a look at the ROM BAR?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-31 9:28 ` Avi Kivity
@ 2009-01-03 2:29 ` Shan, Haitao
2009-01-04 2:12 ` Shan, Haitao
1 sibling, 0 replies; 19+ messages in thread
From: Shan, Haitao @ 2009-01-03 2:29 UTC (permalink / raw)
To: Avi Kivity; +Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Thanks, Avi!
I will definitely double check it when I go back to work tomorrow.
Happy New Year! :)
Best Regards
Shan Haitao
-----Original Message-----
From: Avi Kivity [mailto:avi@redhat.com]
Sent: 2008年12月31日 17:29
To: Shan, Haitao
Cc: Liu, Kechao; 'kvm@vger.kernel.org'
Subject: Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
Shan, Haitao wrote:
> Hi, Avi,
>
> Option ROM already has its own BAR at 0x30h. I think the devices assignment code now already handles this register.
>
Okay good.
> Can I summary your proposals like the following: In guest BIOS, scan all the pci devices (virtual) for existance of Option ROMs. Copy them to available BIOS space in 0xC0000 - 0xDFFFF. Execute the ROM code at copied location.
>
Yes.
> I don't understand why this makes differences compared to scanning and copying Option ROMs in QEMU,
If the ROM BAR is already handled (including registering the memory when
the BAR is programmed -- I don't see that in the code), then there is no
big advantage. It's closer to how real hardware works, but that's about it.
> especially given that the VGA BIOS and etherboot ROM are also copied to BIOS space in QEMU before they execute in rom_scan loop in guest BIOS.
>
The VGA BIOS is typically present on the motherboard itself, at least on
some configurations. You're right about etherboot.
I'll apply the patch. Can you take a look at the ROM BAR?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2008-12-31 9:28 ` Avi Kivity
2009-01-03 2:29 ` Shan, Haitao
@ 2009-01-04 2:12 ` Shan, Haitao
2009-01-04 3:54 ` Leendert van Doorn
1 sibling, 1 reply; 19+ messages in thread
From: Shan, Haitao @ 2009-01-04 2:12 UTC (permalink / raw)
To: 'Avi Kivity'; +Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Sorry I made some mistakes concerning ROM BAR.
Although pci_default_write_config supports ROM BAR write emulation, assigned_dev_pci_write_config does not invoke that function. The little patch below shows where and when the function will be invoked.
Also initial mapping for ROM BAR is not setup in init_assigned_device. However, I do not why they are not there.
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..129d350 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -237,7 +237,7 @@ static void assigned_dev_pci_write_config(PCIDevice *d, uint
/* Continue to program the card */
}
- if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
address == 0x3c || address == 0x3d) {
/* used for update-mappings (BAR emulation) */
pci_default_write_config(d, address, val, len);
Best Regards
Haitao Shan
Avi Kivity wrote:
> Shan, Haitao wrote:
>> Hi, Avi,
>>
>> Option ROM already has its own BAR at 0x30h. I think the devices
>> assignment code now already handles this register.
>>
>
> Okay good.
>
>> Can I summary your proposals like the following: In guest BIOS, scan
>> all the pci devices (virtual) for existance of Option ROMs. Copy
>> them to available BIOS space in 0xC0000 - 0xDFFFF. Execute the ROM
>> code at copied location.
>>
>
> Yes.
>
>> I don't understand why this makes differences compared to scanning
>> and copying Option ROMs in QEMU,
>
> If the ROM BAR is already handled (including registering the memory
> when the BAR is programmed -- I don't see that in the code), then
> there is no big advantage. It's closer to how real hardware works,
> but that's about it.
>
>> especially given that the VGA BIOS and etherboot ROM are also copied
>> to BIOS space in QEMU before they execute in rom_scan loop in guest
>> BIOS.
>>
>
> The VGA BIOS is typically present on the motherboard itself, at least
> on some configurations. You're right about etherboot.
>
> I'll apply the patch. Can you take a look at the ROM BAR?
^ permalink raw reply related [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 2:12 ` Shan, Haitao
@ 2009-01-04 3:54 ` Leendert van Doorn
2009-01-04 4:58 ` Shan, Haitao
2009-01-04 10:26 ` Avi Kivity
0 siblings, 2 replies; 19+ messages in thread
From: Leendert van Doorn @ 2009-01-04 3:54 UTC (permalink / raw)
To: 'Shan, Haitao', 'Avi Kivity'; +Cc: 'Liu, Kechao', kvm
[-- Attachment #1: Type: text/plain, Size: 26013 bytes --]
I've been experimenting with passthru graphics for KVM and for that I needed
to add PCI ROM support to QEMU. I went a slightly different route than
Haitoa, I enabled the BIOS to do the PCI ROM mapping and initialization and
added support for QEMU to fully implement the ROM BAR. This patch also uses
the host BAR addresses in the guest for direct assigned devices, because (as
Avi already pointed out) this is needed for more complex PCI cards such as
graphics adapters. My patch also allows you to optionally specify the ROM
image, which I found very useful for debugging.
My ROMBIOS patches are probably more invasive than necessary. I found it
cleaner (==easier to understand) to call the rombios from 32-bit mode than
leave some droppings (option rom address, bdf) in the BIOS extended data
segment. The other thing you notice is that I initialize the primary VGA ROM
last. I found that this was necessary when emulating dual headed displays
(one display on qemu's emulation window, the other the actual device).
I've attached my patches.
Note that I have not included the full graphics passthru patches yet. I have
that working for the xorg ati drivers (and ATI's ATOMBIOS) but not for the
closed sourced or Windows drivers yet. Let me know if you are interested in
those patches.
Leendert
diff --git a/bios/rombios.c b/bios/rombios.c
index b7a240f..c8ef8ac 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10130,7 +10130,7 @@ rombios32_real_mode:
ret
rombios32_gdt_48:
- dw 0x30
+ dw 0x38
dw rombios32_gdt
dw 0x000f
@@ -10141,6 +10141,7 @@ rombios32_gdt:
dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000
limit=0xffff
dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
+ dw 0xffff, 0, 0x9b0e, 0x0000 ; 16 bit code segment base=0xE0000
limit=0xffff
#endif // BX_ROMBIOS32
@@ -10194,6 +10195,7 @@ no_serial:
pop dx
ret
+#if !BX_ROMBIOS32
rom_checksum:
push ax
push bx
@@ -10266,7 +10268,6 @@ block_count_rounded:
mov ax, #0xf000
mov es, ax
lea di, pnp_string
-
mov bp, sp ;; Call ROM init routine using seg:off on stack
db 0xff ;; call_far ss:[bp+0]
db 0x5e
@@ -10348,6 +10349,7 @@ rom_scan_increment:
xor ax, ax ;; Restore DS back to 0000:
mov ds, ax
ret
+#endif /* !BX_ROMBIOS32 */
post_enable_cache:
;; enable cache
@@ -10652,12 +10654,6 @@ post_default_ints:
;; PIC
call post_init_pic
- mov cx, #0xc000 ;; init vga bios
- mov ax, #0xc780
- call rom_scan
-
- call _print_bios_banner
-
#if BX_ROMBIOS32
call rombios32_init
#else
@@ -10665,7 +10661,12 @@ post_default_ints:
call pcibios_init_iomem_bases
call pcibios_init_irqs
#endif //BX_PCIBIOS
-#endif
+ mov cx, #0xc000 ;; init vga bios
+ mov ax, #0xc780
+ call rom_scan
+#endif //BX_ROMBIOS32
+
+ call _print_bios_banner
;;
;; Floppy setup
@@ -10698,9 +10699,11 @@ post_default_ints:
call _init_boot_vectors
+#if !BX_ROMBIOS32
mov cx, #0xc800 ;; init option roms
mov ax, #0xe000
call rom_scan
+#endif
#if BX_ELTORITO_BOOT
call _interactive_bootkey
diff --git a/bios/rombios32.c b/bios/rombios32.c
index 321563d..580aa17 100755
--- a/bios/rombios32.c
+++ b/bios/rombios32.c
@@ -1,4 +1,4 @@
-/////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
// $Id: rombios32.c,v 1.11 2007/08/03 13:56:13 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
@@ -679,6 +679,7 @@ typedef struct PCIDevice {
int devfn;
} PCIDevice;
+static int pci_bios_nvga = 0;
static uint32_t pci_bios_io_addr;
static uint32_t pci_bios_mem_addr;
static uint32_t pci_bios_bigmem_addr;
@@ -736,7 +737,7 @@ static void pci_set_io_region_addr(PCIDevice *d, int
region_num, uint32_t addr)
old_addr = pci_config_readl(d, ofs);
pci_config_writel(d, ofs, addr);
- BX_INFO("region %d: 0x%08x\n", region_num, addr);
+// BX_INFO("region %d: 0x%08x\n", region_num, addr);
/* enable memory mappings */
cmd = pci_config_readw(d, PCI_COMMAND);
@@ -928,6 +929,7 @@ static void pci_bios_init_device(PCIDevice *d)
}
break;
case 0x0300: /* Display controller - VGA compatible controller */
+ pci_bios_nvga++;
if (vendor_id != 0x1234)
goto default_map;
/* VGA: map frame buffer to default Bochs VBE address */
@@ -953,18 +955,21 @@ static void pci_bios_init_device(PCIDevice *d)
default_map:
/* default memory mappings */
for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ uint32_t orig, addr, val, size;
int ofs;
- uint32_t val, size ;
if (i == PCI_ROM_SLOT)
ofs = 0x30;
else
ofs = 0x10 + i * 4;
+ orig = pci_config_readl(d, ofs);
pci_config_writel(d, ofs, 0xffffffff);
val = pci_config_readl(d, ofs);
if (val != 0) {
size = (~(val & ~0xf)) + 1;
- if (val & PCI_ADDRESS_SPACE_IO)
+ if (i == PCI_ROM_SLOT)
+ paddr = &pci_bios_mem_addr;
+ else if (val & PCI_ADDRESS_SPACE_IO)
paddr = &pci_bios_io_addr;
else if (size >= 0x04000000)
paddr = &pci_bios_bigmem_addr;
@@ -972,7 +977,24 @@ static void pci_bios_init_device(PCIDevice *d)
paddr = &pci_bios_mem_addr;
*paddr = (*paddr + size - 1) & ~(size - 1);
pci_set_io_region_addr(d, i, *paddr);
+ BX_INFO("PCI: region %d: paddr 0x%x, size 0x%x\n",
+ i, *paddr, size);
*paddr += size;
+
+ /*
+ * Some complex PCI devices (such as graphic controllers)
+ * keep lots of internal state, including their PCI mappings
+ * that were determined at initial PCI configuration.
+ * To make them work under QEMU/KVM, we preserve the host's
+ * PCI mappings in the guest.
+ * (we have to do this twice to ensure we update the
mappings)
+ */
+ addr = orig & ~(size - 1);
+ if (addr) {
+ pci_set_io_region_addr(d, i, addr);
+ BX_INFO("PCI: region %d: addr 0x%x, size 0x%x (host)\n",
+ i, addr, size);
+ }
}
}
break;
@@ -1033,6 +1055,310 @@ void pci_bios_init(void)
}
/****************************************************/
+/* Option ROM init */
+
+#define ROM_SIGNATURE 0xAA55
+#define PCI_SIGNATURE 0x52494350 /* "RICP" */
+
+#define VGA_ROM 0xC0000
+#define EXT_ROM_START 0xD0000
+#define EXT_ROM_END 0xE0000
+
+#define ROUND(v, n) (((v) + (n) - 1) & ~((n) - 1))
+
+struct rom_header {
+ uint16_t signature;
+ uint8_t size;
+ uint8_t init[3];
+ uint8_t reserved[0x12];
+ uint16_t data;
+};
+
+struct pci_header {
+ uint32_t signature;
+ uint16_t vendor;
+ uint16_t device;
+ uint16_t reserved_1;
+ uint16_t dlen;
+ uint8_t drevision;
+ uint8_t class_lo;
+ uint16_t class_hi;
+ uint16_t ilen;
+ uint16_t irevision;
+ uint8_t type;
+ uint8_t indicator;
+ uint16_t reserved_2;
+};
+
+struct dtr {
+ unsigned short size;
+ unsigned long base __attribute__ ((packed));
+};
+
+struct dtr oldidtr;
+unsigned long stack;
+
+void rom_far_call(uint32_t seg, uint32_t eax)
+{
+ BX_INFO("ROM far call 0x%x:3; eax=0x%lx\n", seg, eax);
+
+ __asm__ __volatile__ (
+ " .set RELOCBASE, 0xE0000 \n" /* we are executing @0xE0000
*/
+ " .set RELOCSEG, [RELOCBASE>>4]\n"
+ " .set IPL_SEG, 0x9ff0 \n"
+ " .set IPL_TABLE_ENTRIES, 8\n"
+ " .set IPL_COUNT_OFFSET, 0x0080\n"
+ " .set IPL_TYPE_BEV, 0x80\n"
+
+
+ /* save the world */
+ " pushal \n"
+ " mov %esp, stack \n"
+
+ /* get arguments */
+ " movl 8(%ebp), %ecx \n" /* segment */
+ " movl 12(%ebp), %eax \n" /* eax */
+
+ /* save idt */
+ " sidt oldidtr \n"
+
+ /* get into real mode */
+ " ljmp $0x30, $1f-RELOCBASE\n"
+
+ /* initialized segment registers */
+ " .code16 \n"
+ "1: mov $0x28, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ /* turn off protected mode */
+ " movl %cr0, %ebx \n"
+ " andl $~1, %ebx \n"
+ " movl %ebx, %cr0 \n"
+
+ /* flush prefetch queue */
+ " data32 ljmp $RELOCSEG, $(1f-RELOCBASE)\n"
+
+ /* setup 16-bit IDT */
+ " .align 4 \n"
+ "idtr: .word 1024-1 \n"
+ " .long 0 \n"
+ "1: addr32 lidt %cs:(idtr-RELOCBASE)\n"
+
+ /* zero the segment registers */
+ " xor %bx, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ /* setup a new stack */
+ " movl $0x1000, %ebx \n"
+ " movl %ebx, %esp \n"
+
+ /*
+ * 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.
+ */
+ " movw $RELOCBASE>>4, %bx\n"
+ " mov %bx, %es \n"
+ " movl $pnp-RELOCBASE, %edi\n"
+
+ /* far call to extension ROM at %cx:3 */
+ " push %cx \n"
+ " push $3 \n"
+ " mov %sp, %bp \n"
+ " .byte 0xFF, 0x5E, 0x00\n" /* call far %ss:(%bp) */
+ " cli \n" /* just in case */
+
+ /*
+ * Look at the ROM's PnP Expansion header. Properly, we're supposed
+ * to init all the ROMs and then go back and build an IPL table of
+ * all the bootable devices, but we can get away with one pass.
+ */
+ " pop %cx \n"
+ " pop %cx \n" /* ROM segment */
+ " mov %cx, %ds \n"
+
+ " mov $0x1A, %bx \n"
+ " movl (%bx), %eax \n"
+ " movl $pnp-RELOCBASE, %edi\n"
+ " cmpl %eax, (%edi) \n"
+ " je 1f \n"
+ " mov 0x1A(%bx), %ax \n"
+ " cmp $0, %ax \n"
+ " je 1f \n"
+
+ /*
+ * Found a device that thinks it can boot the system.
+ * Record its BEV and product name string.
+ */
+ " mov 0x10(%bx), %di \n"
+ " mov $IPL_SEG, %bx \n"
+ " mov %bx, %ds \n"
+ " mov IPL_COUNT_OFFSET, %bx\n"
+ " cmpw $IPL_TABLE_ENTRIES, %bx\n"
+ " je 1f \n"
+
+ " shl $4, %bx \n"
+ " movw $IPL_TYPE_BEV, (%bx)\n"
+ " mov %ax, 4(%bx) \n"
+ " mov %cx, 6(%bx) \n"
+ " cmp $0, %di \n"
+ " je 2f \n"
+
+ " mov %di, 8(%bx) \n"
+ " mov %cx, 10(%bx) \n"
+
+ "2: shr $4, %bx \n"
+ " inc %bx \n"
+ " mov %bx, IPL_COUNT_OFFSET\n"
+
+ " jmp 1f \n"
+ "pnp: .asciz \"$PnP\" \n"
+
+ /* turn on protected mode */
+ "1: movl %cr0, %ebx \n"
+ " orl $0x1, %ebx \n"
+ " movl %ebx, %cr0 \n"
+
+ /* jump to 32-bit */
+ " data32 ljmp $0x10, $1f \n"
+
+ " .code32 \n"
+ "1: movw $0x18, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ " lidt oldidtr \n"
+ " mov stack, %esp \n"
+ " popal \n"
+ );
+}
+
+static int is_valid_rom(uint32_t rom_address)
+{
+ struct rom_header *rom_header;
+ unsigned char sum = 0, *rom_bytes;
+ unsigned int i;
+
+ rom_header = (struct rom_header *)rom_address;
+ if (rom_header->signature != ROM_SIGNATURE)
+ return 0;
+
+ rom_bytes = (unsigned char *)rom_address;
+ for (i = 0; i < rom_header->size * 512; i++)
+ sum += *(rom_bytes + i);
+ if (sum != 0)
+ return 0;
+
+ return 1;
+}
+
+static int is_valid_pci_rom(PCIDevice *d, uint32_t rom_address)
+{
+ int vendor_id, device_id;
+ struct pci_header *pci_header;
+ struct rom_header *rom_header;
+
+ if (!is_valid_rom(rom_address))
+ return 0;
+
+ rom_header = (struct rom_header *)rom_address;
+ pci_header = (struct pci_header *)((unsigned char *)rom_header
+ + rom_header->data);
+ if ( pci_header->signature != PCI_SIGNATURE)
+ return 0;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ if (pci_header->vendor != vendor_id || pci_header->device != device_id)
+ return 0;
+
+ return 1;
+}
+
+static int pci_nvga = 0;
+static uint32_t pci_vga_devfn = 0;
+uint32_t ext_rom = EXT_ROM_START;
+uint32_t ext_rom_end = EXT_ROM_END;
+
+static void init_rom(PCIDevice *d)
+{
+ struct rom_header *rom_header;
+ struct pci_header *pci_header;
+ uint32_t rom_address;
+ int is_primary_vga = 0;
+ uint8_t *dest;
+ int class;
+
+ class = pci_config_readw(d, PCI_CLASS_DEVICE);
+
+ /* first VGA card found it the primary */
+ if (class == 0x300 && pci_nvga++ == 0)
+ is_primary_vga = 1;
+
+ rom_address = pci_config_readl(d, 0x30) & ~1;
+ if (rom_address == 0 || rom_address == ~0)
+ return;
+
+ BX_INFO("%x:%x: PCI option ROM address 0x%x\n",
+ d->bus, d->devfn, rom_address);
+ pci_config_writel(d, 0x30, rom_address | 1); /* enable rom */
+
+ if (!is_valid_pci_rom(d, rom_address))
+ goto bailout;
+
+ rom_header = (struct rom_header *)rom_address;
+ pci_header = (struct pci_header *)((unsigned char *)rom_header
+ + rom_header->data);
+
+ dest = is_primary_vga ? (uint8_t *)VGA_ROM : (uint8_t *)ext_rom;
+ BX_INFO("Copying PCI %04x:%04x ROM to 0x%x ... ",
+ pci_header->vendor, pci_header->device, dest);
+ memcpy(dest, (char *)rom_address, rom_header->size * 512);
+ BX_INFO("DONE\n");
+
+ if (!is_primary_vga) {
+ rom_far_call(ext_rom >> 4, (d->bus << 8) | d->devfn);
+ ext_rom += ROUND(rom_header->size * 512, 2048);
+ } else
+ pci_vga_devfn = (d->bus << 8) | d->devfn;
+
+bailout:
+ pci_config_writel(d, 0x30, rom_address); /* disable rom */
+}
+
+void option_rom_init(void)
+{
+ /* Execute any extension roms that were pre-loaded */
+ while (ext_rom < ext_rom_end) {
+ struct rom_header *rom_header = (struct rom_header *)ext_rom;
+
+ if (!is_valid_rom(ext_rom))
+ break;
+ rom_far_call(ext_rom >> 4, 0);
+
+ ext_rom += ROUND(rom_header->size * 512, 2048);
+ }
+
+ /* Execute any extension roms that came from PCI devices */
+ pci_nvga = 0;
+ pci_for_each_device(init_rom);
+
+ /* Initialize the primary VGA subsystem (last) */
+ if (is_valid_rom(VGA_ROM))
+ rom_far_call(VGA_ROM >> 4, pci_vga_devfn);
+}
+
+/****************************************************/
/* Multi Processor table init */
static void putb(uint8_t **pp, int val)
@@ -2257,4 +2583,6 @@ void rombios32_init(uint32_t *s3_resume_vector,
uint8_t *shutdown_flag)
BX_PANIC("ebda_cur_addr overflow!\n");
#endif
}
+
+ option_rom_init();
}
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..b6ba676 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -175,13 +175,33 @@ static void assigned_dev_iomem_map(PCIDevice *pci_dev,
int region_num,
}
}
+static void assigned_dev_ioperm(int start_port, int num, int turn_on)
+{
+ struct ioperm_data *data;
+ CPUState *env;
+
+ data = qemu_mallocz(sizeof(struct ioperm_data));
+ if (data == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", __func__);
+ exit(1);
+ }
+
+ data->start_port = start_port;
+ data->num = num;
+ data->turn_on = 1;
+
+ kvm_add_ioperm_data(data);
+
+ for (env = first_cpu; env; env = env->next_cpu)
+ kvm_ioperm(env, data);
+}
+
static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
AssignedDevice *r_dev = (AssignedDevice *) pci_dev;
AssignedDevRegion *region = &r_dev->v_addrs[region_num];
int first_map = (region->e_size == 0);
- CPUState *env;
region->e_physbase = addr;
region->e_size = size;
@@ -189,24 +209,8 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev,
int region_num,
DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d \n",
addr, region->u.r_baseport, type, size, region_num);
- if (first_map) {
- struct ioperm_data *data;
-
- data = qemu_mallocz(sizeof(struct ioperm_data));
- if (data == NULL) {
- fprintf(stderr, "%s: Out of memory\n", __func__);
- exit(1);
- }
-
- data->start_port = region->u.r_baseport;
- data->num = region->r_size;
- data->turn_on = 1;
-
- kvm_add_ioperm_data(data);
-
- for (env = first_cpu; env; env = env->next_cpu)
- kvm_ioperm(env, data);
- }
+ if (first_map)
+ assigned_dev_ioperm(region->u.r_baseport, region->r_size, 1);
register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
(r_dev->v_addrs + region_num));
@@ -237,8 +241,8 @@ static void assigned_dev_pci_write_config(PCIDevice *d,
uint32_t address,
/* Continue to program the card */
}
- if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
- address == 0x3c || address == 0x3d) {
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
/* used for update-mappings (BAR emulation) */
pci_default_write_config(d, address, val, len);
return;
@@ -270,8 +274,8 @@ static uint32_t assigned_dev_pci_read_config(PCIDevice
*d, uint32_t address,
int fd;
ssize_t ret;
- if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
- address == 0x3c || address == 0x3d) {
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
val = pci_default_read_config(d, address, len);
DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
(d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
@@ -309,6 +313,29 @@ do_log:
return val;
}
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+void
+hexdump(uint8_t *data, int sz)
+{
+ uint8_t *d;
+ int i;
+
+ for (d = data; sz > 0; d += 16, sz -= 16) {
+ int n = sz > 16 ? 16 : sz;
+
+ printf("%08x: ", (unsigned) d);
+ for (i = 0; i < n; i++)
+ printf("%02x%c", d[i], i == 7 ? '-' : ' ');
+ for (; i < 16; i++)
+ printf(" %c", i == 7 ? '-' : ' ');
+ printf(" ");
+ for (i = 0; i < n; i++)
+ printf("%c", d[i] >= ' ' && d[i] <= '~' ? d[i] : '.');
+ printf("\n");
+ }
+}
+#endif /* DEVICE_ASSIGNMENT_DEBUG */
+
static int assigned_dev_register_regions(PCIRegion *io_regions,
unsigned long regions_num,
AssignedDevice *pci_dev)
@@ -321,6 +348,78 @@ static int assigned_dev_register_regions(PCIRegion
*io_regions,
continue;
pci_dev->v_addrs[i].num = i;
+ /* handle ROM region */
+ if (i == PCI_ROM_SLOT) {
+ int t = cur_region->type & IORESOURCE_PREFETCH
+ ? PCI_ADDRESS_SPACE_MEM_PREFETCH
+ : PCI_ADDRESS_SPACE_MEM;
+ int nread, tread = 0;
+
+ /*
+ * Map physical memory. Ideally we mmap the rom image but
+ * since it may interfere with the device's operation (in
+ * case we are reading from a real device) we need to make
+ * a copy.
+ */
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_virtbase =
+ qemu_vmalloc((cur_region->size + 0xFFF) & 0xFFFFF000);
+
+ if (pci_dev->v_addrs[i].u.r_virtbase == NULL) {
+ fprintf(stderr, "%s: Error: Couldn't malloc %d"
+ " bytes!\n", __func__,
+ (cur_region->size + 0xFFF) &
+ 0xFFFFF000);
+ return -1;
+ }
+
+ /* Linux quirk: Enable PCI ROM access */
+ if (!pci_dev->romfile) {
+ write(cur_region->resource_fd, "1\n", 2);
+ lseek(cur_region->resource_fd, 0L, SEEK_SET);
+ }
+
+ /* in option ROM */
+ do {
+ nread = read(cur_region->resource_fd,
+ pci_dev->v_addrs[i].u.r_virtbase + tread,
+ TARGET_PAGE_SIZE);
+ if (nread > 0)
+ tread += nread;
+ } while (nread > 0 && tread < cur_region->size);
+
+ if (nread < 0) {
+ fprintf(stderr, "Error: Couldn't read ROM image\n");
+ return -1;
+ }
+
+ /* Linux quirk: Disable PCI ROM access */
+ if (!pci_dev->romfile) {
+ lseek(cur_region->resource_fd, 0L, SEEK_SET);
+ write(cur_region->resource_fd, "0\n", 2);
+ }
+
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+ printf("PCI ROM:\n");
+ hexdump(pci_dev->v_addrs[i].u.r_virtbase, 64);
+#endif
+
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ /* add offset */
+ pci_dev->v_addrs[i].u.r_virtbase +=
+ (cur_region->base_addr & 0xFFF);
+
+ pci_register_io_region((PCIDevice *) pci_dev, i,
+ cur_region->size, t,
+ assigned_dev_iomem_map);
+
+ /* no need to tell the BIOS about the original ROM location */
+
+ continue;
+ }
+
/* handle memory io regions */
if (cur_region->type & IORESOURCE_MEM) {
int t = cur_region->type & IORESOURCE_PREFETCH
@@ -352,8 +451,14 @@ static int assigned_dev_register_regions(PCIRegion
*io_regions,
pci_register_io_region((PCIDevice *) pci_dev, i,
cur_region->size, t,
assigned_dev_iomem_map);
+
+ /* save original host mapping for BIOS (don't update mappings!)
*/
+ *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) =
+ cpu_to_le32(cur_region->base_addr);
+
continue;
}
+
/* handle port io regions */
pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
@@ -364,6 +469,10 @@ static int assigned_dev_register_regions(PCIRegion
*io_regions,
cur_region->size, PCI_ADDRESS_SPACE_IO,
assigned_dev_ioport_map);
+ /* save original host mapping for BIOS (don't update mappings!) */
+ *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) =
+ cpu_to_le32(cur_region->base_addr);
+
/* not relevant for port io */
pci_dev->v_addrs[i].memory_index = 0;
}
@@ -423,10 +532,20 @@ again:
continue;
if (flags & IORESOURCE_MEM) {
flags &= ~IORESOURCE_IO;
- snprintf(name, sizeof(name), "%sresource%d", dir, r);
- fd = open(name, O_RDWR);
- if (fd == -1)
- continue; /* probably ROM */
+ if (r == PCI_ROM_SLOT && pci_dev->romfile) {
+ /* Use specified ROM instead of the card's */
+ strncpy(name, pci_dev->romfile, sizeof(name));
+ } else {
+ /* Use the card's resources */
+ if (r == PCI_ROM_SLOT)
+ snprintf(name, sizeof(name), "%srom", dir);
+ else
+ snprintf(name, sizeof(name), "%sresource%d", dir, r);
+ }
+ if ((fd = open(name, O_RDWR)) < 0) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
rp->resource_fd = fd;
} else
flags &= ~IORESOURCE_PREFETCH;
@@ -557,6 +676,7 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo
*adev, PCIBus *bus)
adev->assigned_dev = dev;
+ dev->romfile = adev->rom;
if (get_real_device(dev, adev->bus, adev->dev, adev->func)) {
fprintf(stderr, "%s: Error: Couldn't get real device (%s)!\n",
__func__, adev->name);
@@ -606,10 +726,11 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo
*adev, PCIBus *bus)
/*
* Syntax to assign device:
*
- * -pcidevice host=bus:dev.func[,dma=none][,name=Foo]
+ * -pcidevice host=bus:dev.func[,dma=none][,name=Foo][,rom=File]
*
* Example:
* -pcidevice host=00:13.0,dma=pvdma
+ * -pcidevice host=01:00.0,rom=bios/RV610_B16905.bin
*
* dma can currently only be 'none' to disable iommu support.
*/
@@ -618,6 +739,7 @@ AssignedDevInfo *add_assigned_device(const char *arg)
char *cp, *cp1;
char device[8];
char dma[6];
+ char rom[PATH_MAX];
int r;
AssignedDevInfo *adev;
@@ -636,6 +758,10 @@ AssignedDevInfo *add_assigned_device(const char *arg)
if (r && !strncmp(dma, "none", 4))
adev->disable_iommu = 1;
#endif
+ r = get_param_value(rom, sizeof rom, "rom", arg);
+ if (r)
+ adev->rom = strdup(rom);
+
cp = device;
adev->bus = strtoul(cp, &cp1, 16);
if (*cp1 != ':')
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index c8c47d3..3507be9 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -37,7 +37,7 @@
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
/* The number of BARs in the config space header */
-#define MAX_IO_REGIONS (6)
+#define MAX_IO_REGIONS (7)
typedef struct {
int type; /* Memory or port I/O */
@@ -80,6 +80,7 @@ typedef struct {
unsigned char h_busnr;
unsigned int h_devfn;
int bound;
+ char *romfile;
} AssignedDevice;
typedef struct AssignedDevInfo AssignedDevInfo;
@@ -89,6 +90,7 @@ struct AssignedDevInfo {
int bus;
int dev;
int func;
+ char *rom;
AssignedDevice *assigned_dev;
LIST_ENTRY(AssignedDevInfo) next;
int disable_iommu;
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 2d7e7f0..8451534 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -972,6 +972,18 @@ static void pc_init1(ram_addr_t ram_size, int
vga_ram_size,
option_rom_setup_reset(0xd0000 + offset, size);
offset += size;
}
+
+ /* If we are using device assignment then map in the rest of the
+ * extension ROM space so PCI BIOS can use it to load ROMs into.
+ */
+ if (assigned_devices_index && offset < 0x10000) {
+ /* only 0xD0000..0xE0000 is available */
+ int size = 0x10000 - offset;
+ ram_addr_t option_rom_offset = qemu_ram_alloc(size);
+ cpu_register_physical_memory(0xd0000 + offset, size,
+ option_rom_offset /*| IO_MEM_ROM
*/);
+ option_rom_setup_reset(0xd0000 + offset, size);
+ }
}
/* map all the bios at the top of memory */
[-- Attachment #2: pci-rom-bar.patch --]
[-- Type: application/octet-stream, Size: 25528 bytes --]
diff --git a/bios/rombios.c b/bios/rombios.c
index b7a240f..c8ef8ac 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10130,7 +10130,7 @@ rombios32_real_mode:
ret
rombios32_gdt_48:
- dw 0x30
+ dw 0x38
dw rombios32_gdt
dw 0x000f
@@ -10141,6 +10141,7 @@ rombios32_gdt:
dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
+ dw 0xffff, 0, 0x9b0e, 0x0000 ; 16 bit code segment base=0xE0000 limit=0xffff
#endif // BX_ROMBIOS32
@@ -10194,6 +10195,7 @@ no_serial:
pop dx
ret
+#if !BX_ROMBIOS32
rom_checksum:
push ax
push bx
@@ -10266,7 +10268,6 @@ block_count_rounded:
mov ax, #0xf000
mov es, ax
lea di, pnp_string
-
mov bp, sp ;; Call ROM init routine using seg:off on stack
db 0xff ;; call_far ss:[bp+0]
db 0x5e
@@ -10348,6 +10349,7 @@ rom_scan_increment:
xor ax, ax ;; Restore DS back to 0000:
mov ds, ax
ret
+#endif /* !BX_ROMBIOS32 */
post_enable_cache:
;; enable cache
@@ -10652,12 +10654,6 @@ post_default_ints:
;; PIC
call post_init_pic
- mov cx, #0xc000 ;; init vga bios
- mov ax, #0xc780
- call rom_scan
-
- call _print_bios_banner
-
#if BX_ROMBIOS32
call rombios32_init
#else
@@ -10665,7 +10661,12 @@ post_default_ints:
call pcibios_init_iomem_bases
call pcibios_init_irqs
#endif //BX_PCIBIOS
-#endif
+ mov cx, #0xc000 ;; init vga bios
+ mov ax, #0xc780
+ call rom_scan
+#endif //BX_ROMBIOS32
+
+ call _print_bios_banner
;;
;; Floppy setup
@@ -10698,9 +10699,11 @@ post_default_ints:
call _init_boot_vectors
+#if !BX_ROMBIOS32
mov cx, #0xc800 ;; init option roms
mov ax, #0xe000
call rom_scan
+#endif
#if BX_ELTORITO_BOOT
call _interactive_bootkey
diff --git a/bios/rombios32.c b/bios/rombios32.c
index 321563d..580aa17 100755
--- a/bios/rombios32.c
+++ b/bios/rombios32.c
@@ -1,4 +1,4 @@
-/////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
// $Id: rombios32.c,v 1.11 2007/08/03 13:56:13 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
@@ -679,6 +679,7 @@ typedef struct PCIDevice {
int devfn;
} PCIDevice;
+static int pci_bios_nvga = 0;
static uint32_t pci_bios_io_addr;
static uint32_t pci_bios_mem_addr;
static uint32_t pci_bios_bigmem_addr;
@@ -736,7 +737,7 @@ static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr)
old_addr = pci_config_readl(d, ofs);
pci_config_writel(d, ofs, addr);
- BX_INFO("region %d: 0x%08x\n", region_num, addr);
+// BX_INFO("region %d: 0x%08x\n", region_num, addr);
/* enable memory mappings */
cmd = pci_config_readw(d, PCI_COMMAND);
@@ -928,6 +929,7 @@ static void pci_bios_init_device(PCIDevice *d)
}
break;
case 0x0300: /* Display controller - VGA compatible controller */
+ pci_bios_nvga++;
if (vendor_id != 0x1234)
goto default_map;
/* VGA: map frame buffer to default Bochs VBE address */
@@ -953,18 +955,21 @@ static void pci_bios_init_device(PCIDevice *d)
default_map:
/* default memory mappings */
for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ uint32_t orig, addr, val, size;
int ofs;
- uint32_t val, size ;
if (i == PCI_ROM_SLOT)
ofs = 0x30;
else
ofs = 0x10 + i * 4;
+ orig = pci_config_readl(d, ofs);
pci_config_writel(d, ofs, 0xffffffff);
val = pci_config_readl(d, ofs);
if (val != 0) {
size = (~(val & ~0xf)) + 1;
- if (val & PCI_ADDRESS_SPACE_IO)
+ if (i == PCI_ROM_SLOT)
+ paddr = &pci_bios_mem_addr;
+ else if (val & PCI_ADDRESS_SPACE_IO)
paddr = &pci_bios_io_addr;
else if (size >= 0x04000000)
paddr = &pci_bios_bigmem_addr;
@@ -972,7 +977,24 @@ static void pci_bios_init_device(PCIDevice *d)
paddr = &pci_bios_mem_addr;
*paddr = (*paddr + size - 1) & ~(size - 1);
pci_set_io_region_addr(d, i, *paddr);
+ BX_INFO("PCI: region %d: paddr 0x%x, size 0x%x\n",
+ i, *paddr, size);
*paddr += size;
+
+ /*
+ * Some complex PCI devices (such as graphic controllers)
+ * keep lots of internal state, including their PCI mappings
+ * that were determined at initial PCI configuration.
+ * To make them work under QEMU/KVM, we preserve the host's
+ * PCI mappings in the guest.
+ * (we have to do this twice to ensure we update the mappings)
+ */
+ addr = orig & ~(size - 1);
+ if (addr) {
+ pci_set_io_region_addr(d, i, addr);
+ BX_INFO("PCI: region %d: addr 0x%x, size 0x%x (host)\n",
+ i, addr, size);
+ }
}
}
break;
@@ -1033,6 +1055,310 @@ void pci_bios_init(void)
}
/****************************************************/
+/* Option ROM init */
+
+#define ROM_SIGNATURE 0xAA55
+#define PCI_SIGNATURE 0x52494350 /* "RICP" */
+
+#define VGA_ROM 0xC0000
+#define EXT_ROM_START 0xD0000
+#define EXT_ROM_END 0xE0000
+
+#define ROUND(v, n) (((v) + (n) - 1) & ~((n) - 1))
+
+struct rom_header {
+ uint16_t signature;
+ uint8_t size;
+ uint8_t init[3];
+ uint8_t reserved[0x12];
+ uint16_t data;
+};
+
+struct pci_header {
+ uint32_t signature;
+ uint16_t vendor;
+ uint16_t device;
+ uint16_t reserved_1;
+ uint16_t dlen;
+ uint8_t drevision;
+ uint8_t class_lo;
+ uint16_t class_hi;
+ uint16_t ilen;
+ uint16_t irevision;
+ uint8_t type;
+ uint8_t indicator;
+ uint16_t reserved_2;
+};
+
+struct dtr {
+ unsigned short size;
+ unsigned long base __attribute__ ((packed));
+};
+
+struct dtr oldidtr;
+unsigned long stack;
+
+void rom_far_call(uint32_t seg, uint32_t eax)
+{
+ BX_INFO("ROM far call 0x%x:3; eax=0x%lx\n", seg, eax);
+
+ __asm__ __volatile__ (
+ " .set RELOCBASE, 0xE0000 \n" /* we are executing @0xE0000 */
+ " .set RELOCSEG, [RELOCBASE>>4]\n"
+ " .set IPL_SEG, 0x9ff0 \n"
+ " .set IPL_TABLE_ENTRIES, 8\n"
+ " .set IPL_COUNT_OFFSET, 0x0080\n"
+ " .set IPL_TYPE_BEV, 0x80\n"
+
+
+ /* save the world */
+ " pushal \n"
+ " mov %esp, stack \n"
+
+ /* get arguments */
+ " movl 8(%ebp), %ecx \n" /* segment */
+ " movl 12(%ebp), %eax \n" /* eax */
+
+ /* save idt */
+ " sidt oldidtr \n"
+
+ /* get into real mode */
+ " ljmp $0x30, $1f-RELOCBASE\n"
+
+ /* initialized segment registers */
+ " .code16 \n"
+ "1: mov $0x28, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ /* turn off protected mode */
+ " movl %cr0, %ebx \n"
+ " andl $~1, %ebx \n"
+ " movl %ebx, %cr0 \n"
+
+ /* flush prefetch queue */
+ " data32 ljmp $RELOCSEG, $(1f-RELOCBASE)\n"
+
+ /* setup 16-bit IDT */
+ " .align 4 \n"
+ "idtr: .word 1024-1 \n"
+ " .long 0 \n"
+ "1: addr32 lidt %cs:(idtr-RELOCBASE)\n"
+
+ /* zero the segment registers */
+ " xor %bx, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ /* setup a new stack */
+ " movl $0x1000, %ebx \n"
+ " movl %ebx, %esp \n"
+
+ /*
+ * 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.
+ */
+ " movw $RELOCBASE>>4, %bx\n"
+ " mov %bx, %es \n"
+ " movl $pnp-RELOCBASE, %edi\n"
+
+ /* far call to extension ROM at %cx:3 */
+ " push %cx \n"
+ " push $3 \n"
+ " mov %sp, %bp \n"
+ " .byte 0xFF, 0x5E, 0x00\n" /* call far %ss:(%bp) */
+ " cli \n" /* just in case */
+
+ /*
+ * Look at the ROM's PnP Expansion header. Properly, we're supposed
+ * to init all the ROMs and then go back and build an IPL table of
+ * all the bootable devices, but we can get away with one pass.
+ */
+ " pop %cx \n"
+ " pop %cx \n" /* ROM segment */
+ " mov %cx, %ds \n"
+
+ " mov $0x1A, %bx \n"
+ " movl (%bx), %eax \n"
+ " movl $pnp-RELOCBASE, %edi\n"
+ " cmpl %eax, (%edi) \n"
+ " je 1f \n"
+ " mov 0x1A(%bx), %ax \n"
+ " cmp $0, %ax \n"
+ " je 1f \n"
+
+ /*
+ * Found a device that thinks it can boot the system.
+ * Record its BEV and product name string.
+ */
+ " mov 0x10(%bx), %di \n"
+ " mov $IPL_SEG, %bx \n"
+ " mov %bx, %ds \n"
+ " mov IPL_COUNT_OFFSET, %bx\n"
+ " cmpw $IPL_TABLE_ENTRIES, %bx\n"
+ " je 1f \n"
+
+ " shl $4, %bx \n"
+ " movw $IPL_TYPE_BEV, (%bx)\n"
+ " mov %ax, 4(%bx) \n"
+ " mov %cx, 6(%bx) \n"
+ " cmp $0, %di \n"
+ " je 2f \n"
+
+ " mov %di, 8(%bx) \n"
+ " mov %cx, 10(%bx) \n"
+
+ "2: shr $4, %bx \n"
+ " inc %bx \n"
+ " mov %bx, IPL_COUNT_OFFSET\n"
+
+ " jmp 1f \n"
+ "pnp: .asciz \"$PnP\" \n"
+
+ /* turn on protected mode */
+ "1: movl %cr0, %ebx \n"
+ " orl $0x1, %ebx \n"
+ " movl %ebx, %cr0 \n"
+
+ /* jump to 32-bit */
+ " data32 ljmp $0x10, $1f \n"
+
+ " .code32 \n"
+ "1: movw $0x18, %bx \n"
+ " mov %bx, %ds \n"
+ " mov %bx, %es \n"
+ " mov %bx, %ss \n"
+ " mov %bx, %fs \n"
+ " mov %bx, %gs \n"
+
+ " lidt oldidtr \n"
+ " mov stack, %esp \n"
+ " popal \n"
+ );
+}
+
+static int is_valid_rom(uint32_t rom_address)
+{
+ struct rom_header *rom_header;
+ unsigned char sum = 0, *rom_bytes;
+ unsigned int i;
+
+ rom_header = (struct rom_header *)rom_address;
+ if (rom_header->signature != ROM_SIGNATURE)
+ return 0;
+
+ rom_bytes = (unsigned char *)rom_address;
+ for (i = 0; i < rom_header->size * 512; i++)
+ sum += *(rom_bytes + i);
+ if (sum != 0)
+ return 0;
+
+ return 1;
+}
+
+static int is_valid_pci_rom(PCIDevice *d, uint32_t rom_address)
+{
+ int vendor_id, device_id;
+ struct pci_header *pci_header;
+ struct rom_header *rom_header;
+
+ if (!is_valid_rom(rom_address))
+ return 0;
+
+ rom_header = (struct rom_header *)rom_address;
+ pci_header = (struct pci_header *)((unsigned char *)rom_header
+ + rom_header->data);
+ if ( pci_header->signature != PCI_SIGNATURE)
+ return 0;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ if (pci_header->vendor != vendor_id || pci_header->device != device_id)
+ return 0;
+
+ return 1;
+}
+
+static int pci_nvga = 0;
+static uint32_t pci_vga_devfn = 0;
+uint32_t ext_rom = EXT_ROM_START;
+uint32_t ext_rom_end = EXT_ROM_END;
+
+static void init_rom(PCIDevice *d)
+{
+ struct rom_header *rom_header;
+ struct pci_header *pci_header;
+ uint32_t rom_address;
+ int is_primary_vga = 0;
+ uint8_t *dest;
+ int class;
+
+ class = pci_config_readw(d, PCI_CLASS_DEVICE);
+
+ /* first VGA card found it the primary */
+ if (class == 0x300 && pci_nvga++ == 0)
+ is_primary_vga = 1;
+
+ rom_address = pci_config_readl(d, 0x30) & ~1;
+ if (rom_address == 0 || rom_address == ~0)
+ return;
+
+ BX_INFO("%x:%x: PCI option ROM address 0x%x\n",
+ d->bus, d->devfn, rom_address);
+ pci_config_writel(d, 0x30, rom_address | 1); /* enable rom */
+
+ if (!is_valid_pci_rom(d, rom_address))
+ goto bailout;
+
+ rom_header = (struct rom_header *)rom_address;
+ pci_header = (struct pci_header *)((unsigned char *)rom_header
+ + rom_header->data);
+
+ dest = is_primary_vga ? (uint8_t *)VGA_ROM : (uint8_t *)ext_rom;
+ BX_INFO("Copying PCI %04x:%04x ROM to 0x%x ... ",
+ pci_header->vendor, pci_header->device, dest);
+ memcpy(dest, (char *)rom_address, rom_header->size * 512);
+ BX_INFO("DONE\n");
+
+ if (!is_primary_vga) {
+ rom_far_call(ext_rom >> 4, (d->bus << 8) | d->devfn);
+ ext_rom += ROUND(rom_header->size * 512, 2048);
+ } else
+ pci_vga_devfn = (d->bus << 8) | d->devfn;
+
+bailout:
+ pci_config_writel(d, 0x30, rom_address); /* disable rom */
+}
+
+void option_rom_init(void)
+{
+ /* Execute any extension roms that were pre-loaded */
+ while (ext_rom < ext_rom_end) {
+ struct rom_header *rom_header = (struct rom_header *)ext_rom;
+
+ if (!is_valid_rom(ext_rom))
+ break;
+ rom_far_call(ext_rom >> 4, 0);
+
+ ext_rom += ROUND(rom_header->size * 512, 2048);
+ }
+
+ /* Execute any extension roms that came from PCI devices */
+ pci_nvga = 0;
+ pci_for_each_device(init_rom);
+
+ /* Initialize the primary VGA subsystem (last) */
+ if (is_valid_rom(VGA_ROM))
+ rom_far_call(VGA_ROM >> 4, pci_vga_devfn);
+}
+
+/****************************************************/
/* Multi Processor table init */
static void putb(uint8_t **pp, int val)
@@ -2257,4 +2583,6 @@ void rombios32_init(uint32_t *s3_resume_vector, uint8_t *shutdown_flag)
BX_PANIC("ebda_cur_addr overflow!\n");
#endif
}
+
+ option_rom_init();
}
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..b6ba676 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -175,13 +175,33 @@ static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num,
}
}
+static void assigned_dev_ioperm(int start_port, int num, int turn_on)
+{
+ struct ioperm_data *data;
+ CPUState *env;
+
+ data = qemu_mallocz(sizeof(struct ioperm_data));
+ if (data == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", __func__);
+ exit(1);
+ }
+
+ data->start_port = start_port;
+ data->num = num;
+ data->turn_on = 1;
+
+ kvm_add_ioperm_data(data);
+
+ for (env = first_cpu; env; env = env->next_cpu)
+ kvm_ioperm(env, data);
+}
+
static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
AssignedDevice *r_dev = (AssignedDevice *) pci_dev;
AssignedDevRegion *region = &r_dev->v_addrs[region_num];
int first_map = (region->e_size == 0);
- CPUState *env;
region->e_physbase = addr;
region->e_size = size;
@@ -189,24 +209,8 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d \n",
addr, region->u.r_baseport, type, size, region_num);
- if (first_map) {
- struct ioperm_data *data;
-
- data = qemu_mallocz(sizeof(struct ioperm_data));
- if (data == NULL) {
- fprintf(stderr, "%s: Out of memory\n", __func__);
- exit(1);
- }
-
- data->start_port = region->u.r_baseport;
- data->num = region->r_size;
- data->turn_on = 1;
-
- kvm_add_ioperm_data(data);
-
- for (env = first_cpu; env; env = env->next_cpu)
- kvm_ioperm(env, data);
- }
+ if (first_map)
+ assigned_dev_ioperm(region->u.r_baseport, region->r_size, 1);
register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
(r_dev->v_addrs + region_num));
@@ -237,8 +241,8 @@ static void assigned_dev_pci_write_config(PCIDevice *d, uint32_t address,
/* Continue to program the card */
}
- if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
- address == 0x3c || address == 0x3d) {
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
/* used for update-mappings (BAR emulation) */
pci_default_write_config(d, address, val, len);
return;
@@ -270,8 +274,8 @@ static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address,
int fd;
ssize_t ret;
- if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
- address == 0x3c || address == 0x3d) {
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
val = pci_default_read_config(d, address, len);
DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
(d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
@@ -309,6 +313,29 @@ do_log:
return val;
}
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+void
+hexdump(uint8_t *data, int sz)
+{
+ uint8_t *d;
+ int i;
+
+ for (d = data; sz > 0; d += 16, sz -= 16) {
+ int n = sz > 16 ? 16 : sz;
+
+ printf("%08x: ", (unsigned) d);
+ for (i = 0; i < n; i++)
+ printf("%02x%c", d[i], i == 7 ? '-' : ' ');
+ for (; i < 16; i++)
+ printf(" %c", i == 7 ? '-' : ' ');
+ printf(" ");
+ for (i = 0; i < n; i++)
+ printf("%c", d[i] >= ' ' && d[i] <= '~' ? d[i] : '.');
+ printf("\n");
+ }
+}
+#endif /* DEVICE_ASSIGNMENT_DEBUG */
+
static int assigned_dev_register_regions(PCIRegion *io_regions,
unsigned long regions_num,
AssignedDevice *pci_dev)
@@ -321,6 +348,78 @@ static int assigned_dev_register_regions(PCIRegion *io_regions,
continue;
pci_dev->v_addrs[i].num = i;
+ /* handle ROM region */
+ if (i == PCI_ROM_SLOT) {
+ int t = cur_region->type & IORESOURCE_PREFETCH
+ ? PCI_ADDRESS_SPACE_MEM_PREFETCH
+ : PCI_ADDRESS_SPACE_MEM;
+ int nread, tread = 0;
+
+ /*
+ * Map physical memory. Ideally we mmap the rom image but
+ * since it may interfere with the device's operation (in
+ * case we are reading from a real device) we need to make
+ * a copy.
+ */
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_virtbase =
+ qemu_vmalloc((cur_region->size + 0xFFF) & 0xFFFFF000);
+
+ if (pci_dev->v_addrs[i].u.r_virtbase == NULL) {
+ fprintf(stderr, "%s: Error: Couldn't malloc %d"
+ " bytes!\n", __func__,
+ (cur_region->size + 0xFFF) &
+ 0xFFFFF000);
+ return -1;
+ }
+
+ /* Linux quirk: Enable PCI ROM access */
+ if (!pci_dev->romfile) {
+ write(cur_region->resource_fd, "1\n", 2);
+ lseek(cur_region->resource_fd, 0L, SEEK_SET);
+ }
+
+ /* in option ROM */
+ do {
+ nread = read(cur_region->resource_fd,
+ pci_dev->v_addrs[i].u.r_virtbase + tread,
+ TARGET_PAGE_SIZE);
+ if (nread > 0)
+ tread += nread;
+ } while (nread > 0 && tread < cur_region->size);
+
+ if (nread < 0) {
+ fprintf(stderr, "Error: Couldn't read ROM image\n");
+ return -1;
+ }
+
+ /* Linux quirk: Disable PCI ROM access */
+ if (!pci_dev->romfile) {
+ lseek(cur_region->resource_fd, 0L, SEEK_SET);
+ write(cur_region->resource_fd, "0\n", 2);
+ }
+
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+ printf("PCI ROM:\n");
+ hexdump(pci_dev->v_addrs[i].u.r_virtbase, 64);
+#endif
+
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ /* add offset */
+ pci_dev->v_addrs[i].u.r_virtbase +=
+ (cur_region->base_addr & 0xFFF);
+
+ pci_register_io_region((PCIDevice *) pci_dev, i,
+ cur_region->size, t,
+ assigned_dev_iomem_map);
+
+ /* no need to tell the BIOS about the original ROM location */
+
+ continue;
+ }
+
/* handle memory io regions */
if (cur_region->type & IORESOURCE_MEM) {
int t = cur_region->type & IORESOURCE_PREFETCH
@@ -352,8 +451,14 @@ static int assigned_dev_register_regions(PCIRegion *io_regions,
pci_register_io_region((PCIDevice *) pci_dev, i,
cur_region->size, t,
assigned_dev_iomem_map);
+
+ /* save original host mapping for BIOS (don't update mappings!) */
+ *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) =
+ cpu_to_le32(cur_region->base_addr);
+
continue;
}
+
/* handle port io regions */
pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
@@ -364,6 +469,10 @@ static int assigned_dev_register_regions(PCIRegion *io_regions,
cur_region->size, PCI_ADDRESS_SPACE_IO,
assigned_dev_ioport_map);
+ /* save original host mapping for BIOS (don't update mappings!) */
+ *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) =
+ cpu_to_le32(cur_region->base_addr);
+
/* not relevant for port io */
pci_dev->v_addrs[i].memory_index = 0;
}
@@ -423,10 +532,20 @@ again:
continue;
if (flags & IORESOURCE_MEM) {
flags &= ~IORESOURCE_IO;
- snprintf(name, sizeof(name), "%sresource%d", dir, r);
- fd = open(name, O_RDWR);
- if (fd == -1)
- continue; /* probably ROM */
+ if (r == PCI_ROM_SLOT && pci_dev->romfile) {
+ /* Use specified ROM instead of the card's */
+ strncpy(name, pci_dev->romfile, sizeof(name));
+ } else {
+ /* Use the card's resources */
+ if (r == PCI_ROM_SLOT)
+ snprintf(name, sizeof(name), "%srom", dir);
+ else
+ snprintf(name, sizeof(name), "%sresource%d", dir, r);
+ }
+ if ((fd = open(name, O_RDWR)) < 0) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
rp->resource_fd = fd;
} else
flags &= ~IORESOURCE_PREFETCH;
@@ -557,6 +676,7 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
adev->assigned_dev = dev;
+ dev->romfile = adev->rom;
if (get_real_device(dev, adev->bus, adev->dev, adev->func)) {
fprintf(stderr, "%s: Error: Couldn't get real device (%s)!\n",
__func__, adev->name);
@@ -606,10 +726,11 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
/*
* Syntax to assign device:
*
- * -pcidevice host=bus:dev.func[,dma=none][,name=Foo]
+ * -pcidevice host=bus:dev.func[,dma=none][,name=Foo][,rom=File]
*
* Example:
* -pcidevice host=00:13.0,dma=pvdma
+ * -pcidevice host=01:00.0,rom=bios/RV610_B16905.bin
*
* dma can currently only be 'none' to disable iommu support.
*/
@@ -618,6 +739,7 @@ AssignedDevInfo *add_assigned_device(const char *arg)
char *cp, *cp1;
char device[8];
char dma[6];
+ char rom[PATH_MAX];
int r;
AssignedDevInfo *adev;
@@ -636,6 +758,10 @@ AssignedDevInfo *add_assigned_device(const char *arg)
if (r && !strncmp(dma, "none", 4))
adev->disable_iommu = 1;
#endif
+ r = get_param_value(rom, sizeof rom, "rom", arg);
+ if (r)
+ adev->rom = strdup(rom);
+
cp = device;
adev->bus = strtoul(cp, &cp1, 16);
if (*cp1 != ':')
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index c8c47d3..3507be9 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -37,7 +37,7 @@
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
/* The number of BARs in the config space header */
-#define MAX_IO_REGIONS (6)
+#define MAX_IO_REGIONS (7)
typedef struct {
int type; /* Memory or port I/O */
@@ -80,6 +80,7 @@ typedef struct {
unsigned char h_busnr;
unsigned int h_devfn;
int bound;
+ char *romfile;
} AssignedDevice;
typedef struct AssignedDevInfo AssignedDevInfo;
@@ -89,6 +90,7 @@ struct AssignedDevInfo {
int bus;
int dev;
int func;
+ char *rom;
AssignedDevice *assigned_dev;
LIST_ENTRY(AssignedDevInfo) next;
int disable_iommu;
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 2d7e7f0..8451534 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -972,6 +972,18 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
option_rom_setup_reset(0xd0000 + offset, size);
offset += size;
}
+
+ /* If we are using device assignment then map in the rest of the
+ * extension ROM space so PCI BIOS can use it to load ROMs into.
+ */
+ if (assigned_devices_index && offset < 0x10000) {
+ /* only 0xD0000..0xE0000 is available */
+ int size = 0x10000 - offset;
+ ram_addr_t option_rom_offset = qemu_ram_alloc(size);
+ cpu_register_physical_memory(0xd0000 + offset, size,
+ option_rom_offset /*| IO_MEM_ROM */);
+ option_rom_setup_reset(0xd0000 + offset, size);
+ }
}
/* map all the bios at the top of memory */
^ permalink raw reply related [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 3:54 ` Leendert van Doorn
@ 2009-01-04 4:58 ` Shan, Haitao
2009-01-04 10:26 ` Avi Kivity
1 sibling, 0 replies; 19+ messages in thread
From: Shan, Haitao @ 2009-01-04 4:58 UTC (permalink / raw)
To: 'Leendert van Doorn', 'Avi Kivity'
Cc: Liu, Kechao, 'kvm@vger.kernel.org'
Ah, your approach is much better than mine.
Please ignore ours.
Best Regards
Haitao Shan
Leendert van Doorn wrote:
> I've been experimenting with passthru graphics for KVM and for that I
> needed to add PCI ROM support to QEMU. I went a slightly different
> route than Haitoa, I enabled the BIOS to do the PCI ROM mapping and
> initialization and added support for QEMU to fully implement the ROM
> BAR. This patch also uses the host BAR addresses in the guest for
> direct assigned devices, because (as Avi already pointed out) this is
> needed for more complex PCI cards such as graphics adapters. My patch
> also allows you to optionally specify the ROM image, which I found
> very useful for debugging.
>
> My ROMBIOS patches are probably more invasive than necessary. I found
> it cleaner (==easier to understand) to call the rombios from 32-bit
> mode than leave some droppings (option rom address, bdf) in the BIOS
> extended data segment. The other thing you notice is that I
> initialize the primary VGA ROM last. I found that this was necessary
> when emulating dual headed displays (one display on qemu's emulation
> window, the other the actual device).
>
> I've attached my patches.
>
> Note that I have not included the full graphics passthru patches yet.
> I have that working for the xorg ati drivers (and ATI's ATOMBIOS) but
> not for the closed sourced or Windows drivers yet. Let me know if you
> are interested in those patches.
>
> Leendert
>
>
> diff --git a/bios/rombios.c b/bios/rombios.c
> index b7a240f..c8ef8ac 100644
> --- a/bios/rombios.c
> +++ b/bios/rombios.c
> @@ -10130,7 +10130,7 @@ rombios32_real_mode:
> ret
>
> rombios32_gdt_48:
> - dw 0x30
> + dw 0x38
> dw rombios32_gdt
> dw 0x000f
>
> @@ -10141,6 +10141,7 @@ rombios32_gdt:
> dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
> dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000
> limit=0xffff
> dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0
> limit=0xffff + dw 0xffff, 0, 0x9b0e, 0x0000 ; 16 bit code segment
> base=0xE0000 limit=0xffff
> #endif // BX_ROMBIOS32
>
>
> @@ -10194,6 +10195,7 @@ no_serial:
> pop dx
> ret
>
> +#if !BX_ROMBIOS32
> rom_checksum:
> push ax
> push bx
> @@ -10266,7 +10268,6 @@ block_count_rounded:
> mov ax, #0xf000
> mov es, ax
> lea di, pnp_string
> -
> mov bp, sp ;; Call ROM init routine using seg:off on stack
> db 0xff ;; call_far ss:[bp+0]
> db 0x5e
> @@ -10348,6 +10349,7 @@ rom_scan_increment:
> xor ax, ax ;; Restore DS back to 0000:
> mov ds, ax
> ret
> +#endif /* !BX_ROMBIOS32 */
>
> post_enable_cache:
> ;; enable cache
> @@ -10652,12 +10654,6 @@ post_default_ints:
> ;; PIC
> call post_init_pic
>
> - mov cx, #0xc000 ;; init vga bios
> - mov ax, #0xc780
> - call rom_scan
> -
> - call _print_bios_banner
> -
> #if BX_ROMBIOS32
> call rombios32_init
> #else
> @@ -10665,7 +10661,12 @@ post_default_ints:
> call pcibios_init_iomem_bases
> call pcibios_init_irqs
> #endif //BX_PCIBIOS
> -#endif
> + mov cx, #0xc000 ;; init vga bios
> + mov ax, #0xc780
> + call rom_scan
> +#endif //BX_ROMBIOS32
> +
> + call _print_bios_banner
>
> ;;
> ;; Floppy setup
> @@ -10698,9 +10699,11 @@ post_default_ints:
>
> call _init_boot_vectors
>
> +#if !BX_ROMBIOS32
> mov cx, #0xc800 ;; init option roms
> mov ax, #0xe000
> call rom_scan
> +#endif
>
> #if BX_ELTORITO_BOOT
> call _interactive_bootkey
> diff --git a/bios/rombios32.c b/bios/rombios32.c
> index 321563d..580aa17 100755
> --- a/bios/rombios32.c
> +++ b/bios/rombios32.c
> @@ -1,4 +1,4 @@
> -/////////////////////////////////////////////////////////////////////////
> +////////////////////////////////////////////////////////////////////////
> // $Id: rombios32.c,v 1.11 2007/08/03 13:56:13 vruppert Exp $
> /////////////////////////////////////////////////////////////////////////
> //
> @@ -679,6 +679,7 @@ typedef struct PCIDevice {
> int devfn;
> } PCIDevice;
>
> +static int pci_bios_nvga = 0;
> static uint32_t pci_bios_io_addr;
> static uint32_t pci_bios_mem_addr;
> static uint32_t pci_bios_bigmem_addr;
> @@ -736,7 +737,7 @@ static void pci_set_io_region_addr(PCIDevice *d,
> int region_num, uint32_t addr)
> old_addr = pci_config_readl(d, ofs);
>
> pci_config_writel(d, ofs, addr);
> - BX_INFO("region %d: 0x%08x\n", region_num, addr);
> +// BX_INFO("region %d: 0x%08x\n", region_num, addr);
>
> /* enable memory mappings */
> cmd = pci_config_readw(d, PCI_COMMAND);
> @@ -928,6 +929,7 @@ static void pci_bios_init_device(PCIDevice *d)
> }
> break;
> case 0x0300: /* Display controller - VGA compatible controller */
> + pci_bios_nvga++;
> if (vendor_id != 0x1234)
> goto default_map;
> /* VGA: map frame buffer to default Bochs VBE address */
> @@ -953,18 +955,21 @@ static void pci_bios_init_device(PCIDevice *d)
> default_map:
> /* default memory mappings */
> for(i = 0; i < PCI_NUM_REGIONS; i++) {
> + uint32_t orig, addr, val, size;
> int ofs;
> - uint32_t val, size ;
>
> if (i == PCI_ROM_SLOT)
> ofs = 0x30;
> else
> ofs = 0x10 + i * 4;
> + orig = pci_config_readl(d, ofs);
> pci_config_writel(d, ofs, 0xffffffff);
> val = pci_config_readl(d, ofs);
> if (val != 0) {
> size = (~(val & ~0xf)) + 1;
> - if (val & PCI_ADDRESS_SPACE_IO)
> + if (i == PCI_ROM_SLOT)
> + paddr = &pci_bios_mem_addr;
> + else if (val & PCI_ADDRESS_SPACE_IO)
> paddr = &pci_bios_io_addr;
> else if (size >= 0x04000000)
> paddr = &pci_bios_bigmem_addr;
> @@ -972,7 +977,24 @@ static void pci_bios_init_device(PCIDevice *d)
> paddr = &pci_bios_mem_addr;
> *paddr = (*paddr + size - 1) & ~(size - 1);
> pci_set_io_region_addr(d, i, *paddr);
> + BX_INFO("PCI: region %d: paddr 0x%x, size 0x%x\n",
> + i, *paddr, size);
> *paddr += size;
> +
> + /*
> + * Some complex PCI devices (such as graphic controllers)
> + * keep lots of internal state, including their PCI mappings
> + * that were determined at initial PCI configuration.
> + * To make them work under QEMU/KVM, we preserve the host's
> + * PCI mappings in the guest.
> + * (we have to do this twice to ensure we update the
> mappings)
> + */
> + addr = orig & ~(size - 1);
> + if (addr) {
> + pci_set_io_region_addr(d, i, addr);
> + BX_INFO("PCI: region %d: addr 0x%x, size 0x%x (host)\n",
> + i, addr, size);
> + }
> }
> }
> break;
> @@ -1033,6 +1055,310 @@ void pci_bios_init(void)
> }
>
> /****************************************************/
> +/* Option ROM init */
> +
> +#define ROM_SIGNATURE 0xAA55
> +#define PCI_SIGNATURE 0x52494350 /* "RICP" */
> +
> +#define VGA_ROM 0xC0000
> +#define EXT_ROM_START 0xD0000
> +#define EXT_ROM_END 0xE0000
> +
> +#define ROUND(v, n) (((v) + (n) - 1) & ~((n) - 1))
> +
> +struct rom_header {
> + uint16_t signature;
> + uint8_t size;
> + uint8_t init[3];
> + uint8_t reserved[0x12];
> + uint16_t data;
> +};
> +
> +struct pci_header {
> + uint32_t signature;
> + uint16_t vendor;
> + uint16_t device;
> + uint16_t reserved_1;
> + uint16_t dlen;
> + uint8_t drevision;
> + uint8_t class_lo;
> + uint16_t class_hi;
> + uint16_t ilen;
> + uint16_t irevision;
> + uint8_t type;
> + uint8_t indicator;
> + uint16_t reserved_2;
> +};
> +
> +struct dtr {
> + unsigned short size;
> + unsigned long base __attribute__ ((packed));
> +};
> +
> +struct dtr oldidtr;
> +unsigned long stack;
> +
> +void rom_far_call(uint32_t seg, uint32_t eax)
> +{
> + BX_INFO("ROM far call 0x%x:3; eax=0x%lx\n", seg, eax);
> +
> + __asm__ __volatile__ (
> + " .set RELOCBASE, 0xE0000 \n" /* we are executing @0xE0000
> */
> + " .set RELOCSEG, [RELOCBASE>>4]\n"
> + " .set IPL_SEG, 0x9ff0 \n"
> + " .set IPL_TABLE_ENTRIES, 8\n"
> + " .set IPL_COUNT_OFFSET, 0x0080\n"
> + " .set IPL_TYPE_BEV, 0x80\n"
> +
> +
> + /* save the world */
> + " pushal \n"
> + " mov %esp, stack \n"
> +
> + /* get arguments */
> + " movl 8(%ebp), %ecx \n" /* segment */
> + " movl 12(%ebp), %eax \n" /* eax */
> +
> + /* save idt */
> + " sidt oldidtr \n"
> +
> + /* get into real mode */
> + " ljmp $0x30, $1f-RELOCBASE\n"
> +
> + /* initialized segment registers */
> + " .code16 \n"
> + "1: mov $0x28, %bx \n"
> + " mov %bx, %ds \n"
> + " mov %bx, %es \n"
> + " mov %bx, %ss \n"
> + " mov %bx, %fs \n"
> + " mov %bx, %gs \n"
> +
> + /* turn off protected mode */
> + " movl %cr0, %ebx \n"
> + " andl $~1, %ebx \n"
> + " movl %ebx, %cr0 \n"
> +
> + /* flush prefetch queue */
> + " data32 ljmp $RELOCSEG, $(1f-RELOCBASE)\n"
> +
> + /* setup 16-bit IDT */
> + " .align 4 \n"
> + "idtr: .word 1024-1 \n"
> + " .long 0 \n"
> + "1: addr32 lidt %cs:(idtr-RELOCBASE)\n"
> +
> + /* zero the segment registers */
> + " xor %bx, %bx \n"
> + " mov %bx, %ds \n"
> + " mov %bx, %es \n"
> + " mov %bx, %ss \n"
> + " mov %bx, %fs \n"
> + " mov %bx, %gs \n"
> +
> + /* setup a new stack */
> + " movl $0x1000, %ebx \n"
> + " movl %ebx, %esp \n"
> +
> + /*
> + * 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.
> + */
> + " movw $RELOCBASE>>4, %bx\n"
> + " mov %bx, %es \n"
> + " movl $pnp-RELOCBASE, %edi\n"
> +
> + /* far call to extension ROM at %cx:3 */
> + " push %cx \n"
> + " push $3 \n"
> + " mov %sp, %bp \n"
> + " .byte 0xFF, 0x5E, 0x00\n" /* call far %ss:(%bp) */
> + " cli \n" /* just in case */
> +
> + /*
> + * Look at the ROM's PnP Expansion header. Properly, we're supposed
> + * to init all the ROMs and then go back and build an IPL table of
> + * all the bootable devices, but we can get away with one pass.
> + */
> + " pop %cx \n"
> + " pop %cx \n" /* ROM segment */
> + " mov %cx, %ds \n"
> +
> + " mov $0x1A, %bx \n"
> + " movl (%bx), %eax \n"
> + " movl $pnp-RELOCBASE, %edi\n"
> + " cmpl %eax, (%edi) \n"
> + " je 1f \n"
> + " mov 0x1A(%bx), %ax \n"
> + " cmp $0, %ax \n"
> + " je 1f \n"
> +
> + /*
> + * Found a device that thinks it can boot the system.
> + * Record its BEV and product name string.
> + */
> + " mov 0x10(%bx), %di \n"
> + " mov $IPL_SEG, %bx \n"
> + " mov %bx, %ds \n"
> + " mov IPL_COUNT_OFFSET, %bx\n"
> + " cmpw $IPL_TABLE_ENTRIES, %bx\n"
> + " je 1f \n"
> +
> + " shl $4, %bx \n"
> + " movw $IPL_TYPE_BEV, (%bx)\n"
> + " mov %ax, 4(%bx) \n"
> + " mov %cx, 6(%bx) \n"
> + " cmp $0, %di \n"
> + " je 2f \n"
> +
> + " mov %di, 8(%bx) \n"
> + " mov %cx, 10(%bx) \n"
> +
> + "2: shr $4, %bx \n"
> + " inc %bx \n"
> + " mov %bx, IPL_COUNT_OFFSET\n"
> +
> + " jmp 1f \n"
> + "pnp: .asciz \"$PnP\" \n"
> +
> + /* turn on protected mode */
> + "1: movl %cr0, %ebx \n"
> + " orl $0x1, %ebx \n"
> + " movl %ebx, %cr0 \n"
> +
> + /* jump to 32-bit */
> + " data32 ljmp $0x10, $1f \n"
> +
> + " .code32 \n"
> + "1: movw $0x18, %bx \n"
> + " mov %bx, %ds \n"
> + " mov %bx, %es \n"
> + " mov %bx, %ss \n"
> + " mov %bx, %fs \n"
> + " mov %bx, %gs \n"
> +
> + " lidt oldidtr \n"
> + " mov stack, %esp \n"
> + " popal \n"
> + );
> +}
> +
> +static int is_valid_rom(uint32_t rom_address)
> +{
> + struct rom_header *rom_header;
> + unsigned char sum = 0, *rom_bytes;
> + unsigned int i;
> +
> + rom_header = (struct rom_header *)rom_address;
> + if (rom_header->signature != ROM_SIGNATURE)
> + return 0;
> +
> + rom_bytes = (unsigned char *)rom_address;
> + for (i = 0; i < rom_header->size * 512; i++)
> + sum += *(rom_bytes + i);
> + if (sum != 0)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int is_valid_pci_rom(PCIDevice *d, uint32_t rom_address)
> +{
> + int vendor_id, device_id;
> + struct pci_header *pci_header;
> + struct rom_header *rom_header;
> +
> + if (!is_valid_rom(rom_address))
> + return 0;
> +
> + rom_header = (struct rom_header *)rom_address;
> + pci_header = (struct pci_header *)((unsigned char *)rom_header
> + + rom_header->data);
> + if ( pci_header->signature != PCI_SIGNATURE)
> + return 0;
> +
> + vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
> + device_id = pci_config_readw(d, PCI_DEVICE_ID);
> + if (pci_header->vendor != vendor_id || pci_header->device !=
> device_id) + return 0;
> +
> + return 1;
> +}
> +
> +static int pci_nvga = 0;
> +static uint32_t pci_vga_devfn = 0;
> +uint32_t ext_rom = EXT_ROM_START;
> +uint32_t ext_rom_end = EXT_ROM_END;
> +
> +static void init_rom(PCIDevice *d)
> +{
> + struct rom_header *rom_header;
> + struct pci_header *pci_header;
> + uint32_t rom_address;
> + int is_primary_vga = 0;
> + uint8_t *dest;
> + int class;
> +
> + class = pci_config_readw(d, PCI_CLASS_DEVICE);
> +
> + /* first VGA card found it the primary */
> + if (class == 0x300 && pci_nvga++ == 0)
> + is_primary_vga = 1;
> +
> + rom_address = pci_config_readl(d, 0x30) & ~1;
> + if (rom_address == 0 || rom_address == ~0)
> + return;
> +
> + BX_INFO("%x:%x: PCI option ROM address 0x%x\n",
> + d->bus, d->devfn, rom_address);
> + pci_config_writel(d, 0x30, rom_address | 1); /* enable rom */
> +
> + if (!is_valid_pci_rom(d, rom_address))
> + goto bailout;
> +
> + rom_header = (struct rom_header *)rom_address;
> + pci_header = (struct pci_header *)((unsigned char *)rom_header
> + + rom_header->data);
> +
> + dest = is_primary_vga ? (uint8_t *)VGA_ROM : (uint8_t *)ext_rom;
> + BX_INFO("Copying PCI %04x:%04x ROM to 0x%x ... ",
> + pci_header->vendor, pci_header->device, dest);
> + memcpy(dest, (char *)rom_address, rom_header->size * 512);
> + BX_INFO("DONE\n");
> +
> + if (!is_primary_vga) {
> + rom_far_call(ext_rom >> 4, (d->bus << 8) | d->devfn);
> + ext_rom += ROUND(rom_header->size * 512, 2048);
> + } else
> + pci_vga_devfn = (d->bus << 8) | d->devfn;
> +
> +bailout:
> + pci_config_writel(d, 0x30, rom_address); /* disable rom */
> +}
> +
> +void option_rom_init(void)
> +{
> + /* Execute any extension roms that were pre-loaded */
> + while (ext_rom < ext_rom_end) {
> + struct rom_header *rom_header = (struct rom_header *)ext_rom;
> +
> + if (!is_valid_rom(ext_rom))
> + break;
> + rom_far_call(ext_rom >> 4, 0);
> +
> + ext_rom += ROUND(rom_header->size * 512, 2048);
> + }
> +
> + /* Execute any extension roms that came from PCI devices */
> + pci_nvga = 0;
> + pci_for_each_device(init_rom);
> +
> + /* Initialize the primary VGA subsystem (last) */
> + if (is_valid_rom(VGA_ROM))
> + rom_far_call(VGA_ROM >> 4, pci_vga_devfn);
> +}
> +
> +/****************************************************/
> /* Multi Processor table init */
>
> static void putb(uint8_t **pp, int val)
> @@ -2257,4 +2583,6 @@ void rombios32_init(uint32_t *s3_resume_vector,
> uint8_t *shutdown_flag)
> BX_PANIC("ebda_cur_addr overflow!\n");
> #endif
> }
> +
> + option_rom_init();
> }
> diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
> index 7a66665..b6ba676 100644
> --- a/qemu/hw/device-assignment.c
> +++ b/qemu/hw/device-assignment.c
> @@ -175,13 +175,33 @@ static void assigned_dev_iomem_map(PCIDevice
> *pci_dev, int region_num,
> }
> }
>
> +static void assigned_dev_ioperm(int start_port, int num, int turn_on)
> +{
> + struct ioperm_data *data;
> + CPUState *env;
> +
> + data = qemu_mallocz(sizeof(struct ioperm_data));
> + if (data == NULL) {
> + fprintf(stderr, "%s: Out of memory\n", __func__);
> + exit(1);
> + }
> +
> + data->start_port = start_port;
> + data->num = num;
> + data->turn_on = 1;
> +
> + kvm_add_ioperm_data(data);
> +
> + for (env = first_cpu; env; env = env->next_cpu)
> + kvm_ioperm(env, data);
> +}
> +
> static void assigned_dev_ioport_map(PCIDevice *pci_dev, int
> region_num, uint32_t addr,
> uint32_t size, int type) {
> AssignedDevice *r_dev = (AssignedDevice *) pci_dev;
> AssignedDevRegion *region = &r_dev->v_addrs[region_num];
> int first_map = (region->e_size == 0);
> - CPUState *env;
>
> region->e_physbase = addr;
> region->e_size = size;
> @@ -189,24 +209,8 @@ static void assigned_dev_ioport_map(PCIDevice
> *pci_dev, int region_num,
> DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d
> \n", addr, region->u.r_baseport, type, size, region_num);
>
> - if (first_map) {
> - struct ioperm_data *data;
> -
> - data = qemu_mallocz(sizeof(struct ioperm_data));
> - if (data == NULL) {
> - fprintf(stderr, "%s: Out of memory\n", __func__);
> - exit(1);
> - }
> -
> - data->start_port = region->u.r_baseport;
> - data->num = region->r_size;
> - data->turn_on = 1;
> -
> - kvm_add_ioperm_data(data);
> -
> - for (env = first_cpu; env; env = env->next_cpu)
> - kvm_ioperm(env, data);
> - }
> + if (first_map)
> + assigned_dev_ioperm(region->u.r_baseport, region->r_size, 1);
>
> register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
> (r_dev->v_addrs + region_num));
> @@ -237,8 +241,8 @@ static void
> assigned_dev_pci_write_config(PCIDevice *d, uint32_t address,
> /* Continue to program the card */
> }
>
> - if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
> - address == 0x3c || address == 0x3d) {
> + if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
> + address == 0x34 || address == 0x3c || address == 0x3d) {
> /* used for update-mappings (BAR emulation) */
> pci_default_write_config(d, address, val, len);
> return;
> @@ -270,8 +274,8 @@ static uint32_t
> assigned_dev_pci_read_config(PCIDevice *d, uint32_t address,
> int fd;
> ssize_t ret;
>
> - if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
> - address == 0x3c || address == 0x3d) {
> + if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
> + address == 0x34 || address == 0x3c || address == 0x3d) {
> val = pci_default_read_config(d, address, len);
> DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
> (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address,
> val, len); @@ -309,6 +313,29 @@ do_log:
> return val;
> }
>
> +#ifdef DEVICE_ASSIGNMENT_DEBUG
> +void
> +hexdump(uint8_t *data, int sz)
> +{
> + uint8_t *d;
> + int i;
> +
> + for (d = data; sz > 0; d += 16, sz -= 16) {
> + int n = sz > 16 ? 16 : sz;
> +
> + printf("%08x: ", (unsigned) d);
> + for (i = 0; i < n; i++)
> + printf("%02x%c", d[i], i == 7 ? '-' : ' ');
> + for (; i < 16; i++)
> + printf(" %c", i == 7 ? '-' : ' ');
> + printf(" ");
> + for (i = 0; i < n; i++)
> + printf("%c", d[i] >= ' ' && d[i] <= '~' ? d[i] : '.');
> + printf("\n");
> + }
> +}
> +#endif /* DEVICE_ASSIGNMENT_DEBUG */
> +
> static int assigned_dev_register_regions(PCIRegion *io_regions,
> unsigned long regions_num,
> AssignedDevice *pci_dev)
> @@ -321,6 +348,78 @@ static int
> assigned_dev_register_regions(PCIRegion *io_regions,
> continue;
> pci_dev->v_addrs[i].num = i;
>
> + /* handle ROM region */
> + if (i == PCI_ROM_SLOT) {
> + int t = cur_region->type & IORESOURCE_PREFETCH
> + ? PCI_ADDRESS_SPACE_MEM_PREFETCH
> + : PCI_ADDRESS_SPACE_MEM;
> + int nread, tread = 0;
> +
> + /*
> + * Map physical memory. Ideally we mmap the rom image but
> + * since it may interfere with the device's operation (in
> + * case we are reading from a real device) we need to make
> + * a copy.
> + */
> + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
> + pci_dev->v_addrs[i].u.r_virtbase =
> + qemu_vmalloc((cur_region->size + 0xFFF) & 0xFFFFF000);
> +
> + if (pci_dev->v_addrs[i].u.r_virtbase == NULL) {
> + fprintf(stderr, "%s: Error: Couldn't malloc %d"
> + " bytes!\n", __func__,
> + (cur_region->size + 0xFFF) &
> + 0xFFFFF000);
> + return -1;
> + }
> +
> + /* Linux quirk: Enable PCI ROM access */
> + if (!pci_dev->romfile) {
> + write(cur_region->resource_fd, "1\n", 2);
> + lseek(cur_region->resource_fd, 0L, SEEK_SET);
> + }
> +
> + /* in option ROM */
> + do {
> + nread = read(cur_region->resource_fd,
> + pci_dev->v_addrs[i].u.r_virtbase + tread,
> + TARGET_PAGE_SIZE);
> + if (nread > 0)
> + tread += nread;
> + } while (nread > 0 && tread < cur_region->size);
> +
> + if (nread < 0) {
> + fprintf(stderr, "Error: Couldn't read ROM image\n");
> + return -1;
> + }
> +
> + /* Linux quirk: Disable PCI ROM access */
> + if (!pci_dev->romfile) {
> + lseek(cur_region->resource_fd, 0L, SEEK_SET);
> + write(cur_region->resource_fd, "0\n", 2);
> + }
> +
> +#ifdef DEVICE_ASSIGNMENT_DEBUG
> + printf("PCI ROM:\n");
> + hexdump(pci_dev->v_addrs[i].u.r_virtbase, 64);
> +#endif
> +
> + pci_dev->v_addrs[i].r_size = cur_region->size;
> + pci_dev->v_addrs[i].e_size = 0;
> +
> + /* add offset */
> + pci_dev->v_addrs[i].u.r_virtbase +=
> + (cur_region->base_addr & 0xFFF);
> +
> + pci_register_io_region((PCIDevice *) pci_dev, i,
> + cur_region->size, t,
> + assigned_dev_iomem_map);
> +
> + /* no need to tell the BIOS about the original ROM location */
> +
> + continue;
> + }
> +
> /* handle memory io regions */
> if (cur_region->type & IORESOURCE_MEM) {
> int t = cur_region->type & IORESOURCE_PREFETCH
> @@ -352,8 +451,14 @@ static int
> assigned_dev_register_regions(PCIRegion *io_regions,
> pci_register_io_region((PCIDevice *) pci_dev, i,
> cur_region->size, t,
> assigned_dev_iomem_map);
> +
> + /* save original host mapping for BIOS (don't update
> mappings!) */
> + *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) =
> +
> cpu_to_le32(cur_region->base_addr); +
> continue;
> }
> +
> /* handle port io regions */
> pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
> pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
> @@ -364,6 +469,10 @@ static int
> assigned_dev_register_regions(PCIRegion *io_regions,
> cur_region->size,
> PCI_ADDRESS_SPACE_IO,
> assigned_dev_ioport_map);
>
> + /* save original host mapping for BIOS (don't update
> mappings!) */ + *(uint32_t *)(pci_dev->dev.config + 0x10 + i
> * 4) = +
> cpu_to_le32(cur_region->base_addr); +
> /* not relevant for port io */
> pci_dev->v_addrs[i].memory_index = 0;
> }
> @@ -423,10 +532,20 @@ again:
> continue;
> if (flags & IORESOURCE_MEM) {
> flags &= ~IORESOURCE_IO;
> - snprintf(name, sizeof(name), "%sresource%d", dir, r);
> - fd = open(name, O_RDWR);
> - if (fd == -1)
> - continue; /* probably ROM */
> + if (r == PCI_ROM_SLOT && pci_dev->romfile) {
> + /* Use specified ROM instead of the card's */
> + strncpy(name, pci_dev->romfile, sizeof(name));
> + } else {
> + /* Use the card's resources */
> + if (r == PCI_ROM_SLOT)
> + snprintf(name, sizeof(name), "%srom", dir);
> + else
> + snprintf(name, sizeof(name), "%sresource%d", dir, r);
> + }
> + if ((fd = open(name, O_RDWR)) < 0) {
> + fprintf(stderr, "%s: %s: %m\n", __func__, name);
> + return 1;
> + }
> rp->resource_fd = fd;
> } else
> flags &= ~IORESOURCE_PREFETCH;
> @@ -557,6 +676,7 @@ struct PCIDevice
> *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
>
> adev->assigned_dev = dev;
>
> + dev->romfile = adev->rom;
> if (get_real_device(dev, adev->bus, adev->dev, adev->func)) {
> fprintf(stderr, "%s: Error: Couldn't get real device
> (%s)!\n", __func__, adev->name);
> @@ -606,10 +726,11 @@ struct PCIDevice
> *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
> /*
> * Syntax to assign device:
> *
> - * -pcidevice host=bus:dev.func[,dma=none][,name=Foo]
> + * -pcidevice host=bus:dev.func[,dma=none][,name=Foo][,rom=File]
> *
> * Example:
> * -pcidevice host=00:13.0,dma=pvdma
> + * -pcidevice host=01:00.0,rom=bios/RV610_B16905.bin
> *
> * dma can currently only be 'none' to disable iommu support.
> */
> @@ -618,6 +739,7 @@ AssignedDevInfo *add_assigned_device(const char
> *arg) char *cp, *cp1;
> char device[8];
> char dma[6];
> + char rom[PATH_MAX];
> int r;
> AssignedDevInfo *adev;
>
> @@ -636,6 +758,10 @@ AssignedDevInfo *add_assigned_device(const char
> *arg) if (r && !strncmp(dma, "none", 4))
> adev->disable_iommu = 1;
> #endif
> + r = get_param_value(rom, sizeof rom, "rom", arg);
> + if (r)
> + adev->rom = strdup(rom);
> +
> cp = device;
> adev->bus = strtoul(cp, &cp1, 16);
> if (*cp1 != ':')
> diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
> index c8c47d3..3507be9 100644
> --- a/qemu/hw/device-assignment.h
> +++ b/qemu/hw/device-assignment.h
> @@ -37,7 +37,7 @@
> #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) &
> 0x07))
>
> /* The number of BARs in the config space header */
> -#define MAX_IO_REGIONS (6)
> +#define MAX_IO_REGIONS (7)
>
> typedef struct {
> int type; /* Memory or port I/O */
> @@ -80,6 +80,7 @@ typedef struct {
> unsigned char h_busnr;
> unsigned int h_devfn;
> int bound;
> + char *romfile;
> } AssignedDevice;
>
> typedef struct AssignedDevInfo AssignedDevInfo;
> @@ -89,6 +90,7 @@ struct AssignedDevInfo {
> int bus;
> int dev;
> int func;
> + char *rom;
> AssignedDevice *assigned_dev;
> LIST_ENTRY(AssignedDevInfo) next;
> int disable_iommu;
> diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
> index 2d7e7f0..8451534 100644
> --- a/qemu/hw/pc.c
> +++ b/qemu/hw/pc.c
> @@ -972,6 +972,18 @@ static void pc_init1(ram_addr_t ram_size, int
> vga_ram_size,
> option_rom_setup_reset(0xd0000 + offset, size);
> offset += size;
> }
> +
> + /* If we are using device assignment then map in the rest of
> the + * extension ROM space so PCI BIOS can use it to load
> ROMs into. + */
> + if (assigned_devices_index && offset < 0x10000) {
> + /* only 0xD0000..0xE0000 is available */
> + int size = 0x10000 - offset;
> + ram_addr_t option_rom_offset = qemu_ram_alloc(size);
> + cpu_register_physical_memory(0xd0000 + offset, size,
> + option_rom_offset /*| IO_MEM_ROM
> */);
> + option_rom_setup_reset(0xd0000 + offset, size);
> + }
> }
>
> /* map all the bios at the top of memory */
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 3:54 ` Leendert van Doorn
2009-01-04 4:58 ` Shan, Haitao
@ 2009-01-04 10:26 ` Avi Kivity
2009-01-04 17:12 ` Leendert van Doorn
1 sibling, 1 reply; 19+ messages in thread
From: Avi Kivity @ 2009-01-04 10:26 UTC (permalink / raw)
To: Leendert van Doorn; +Cc: 'Shan, Haitao', 'Liu, Kechao', kvm
Leendert van Doorn wrote:
> I've been experimenting with passthru graphics for KVM and for that I needed
> to add PCI ROM support to QEMU. I went a slightly different route than
> Haitoa, I enabled the BIOS to do the PCI ROM mapping and initialization and
> added support for QEMU to fully implement the ROM BAR. This patch also uses
> the host BAR addresses in the guest for direct assigned devices, because (as
> Avi already pointed out) this is needed for more complex PCI cards such as
> graphics adapters. My patch also allows you to optionally specify the ROM
> image, which I found very useful for debugging.
>
> My ROMBIOS patches are probably more invasive than necessary. I found it
> cleaner (==easier to understand) to call the rombios from 32-bit mode than
> leave some droppings (option rom address, bdf) in the BIOS extended data
> segment. The other thing you notice is that I initialize the primary VGA ROM
> last. I found that this was necessary when emulating dual headed displays
> (one display on qemu's emulation window, the other the actual device).
>
> I've attached my patches.
>
> Note that I have not included the full graphics passthru patches yet. I have
> that working for the xorg ati drivers (and ATI's ATOMBIOS) but not for the
> closed sourced or Windows drivers yet. Let me know if you are interested in
> those patches.
>
>
This is very interesting.
> @@ -10194,6 +10195,7 @@ no_serial:
> pop dx
> ret
>
> +#if !BX_ROMBIOS32
> rom_checksum:
> push ax
> push bx
> @@ -10266,7 +10268,6 @@ block_count_rounded:
> mov ax, #0xf000
> mov es, ax
> lea di, pnp_string
> -
> mov bp, sp ;; Call ROM init routine using seg:off on stack
> db 0xff ;; call_far ss:[bp+0]
> db 0x5e
> @@ -10348,6 +10349,7 @@ rom_scan_increment:
> xor ax, ax ;; Restore DS back to 0000:
> mov ds, ax
> ret
> +#endif /* !BX_ROMBIOS32 */
>
This is worrying as it will cause us to diverge from upstream bochs bios.
>
> post_enable_cache:
> ;; enable cache
> @@ -10652,12 +10654,6 @@ post_default_ints:
> ;; PIC
> call post_init_pic
>
> - mov cx, #0xc000 ;; init vga bios
> - mov ax, #0xc780
> - call rom_scan
> -
> - call _print_bios_banner
> -
> #if BX_ROMBIOS32
> call rombios32_init
> #else
> @@ -10665,7 +10661,12 @@ post_default_ints:
> call pcibios_init_iomem_bases
> call pcibios_init_irqs
> #endif //BX_PCIBIOS
> -#endif
> + mov cx, #0xc000 ;; init vga bios
> + mov ax, #0xc780
> + call rom_scan
> +#endif //BX_ROMBIOS32
> +
> + call _print_bios_banner
>
If one assigns a graphics card, it should replace the cirrus vga bios;
that should make this change unnecessary.
>
> @@ -953,18 +955,21 @@ static void pci_bios_init_device(PCIDevice *d)
> default_map:
> /* default memory mappings */
> for(i = 0; i < PCI_NUM_REGIONS; i++) {
> + uint32_t orig, addr, val, size;
> int ofs;
> - uint32_t val, size ;
>
> if (i == PCI_ROM_SLOT)
> ofs = 0x30;
> else
> ofs = 0x10 + i * 4;
> + orig = pci_config_readl(d, ofs);
> pci_config_writel(d, ofs, 0xffffffff);
> val = pci_config_readl(d, ofs);
> if (val != 0) {
> size = (~(val & ~0xf)) + 1;
> - if (val & PCI_ADDRESS_SPACE_IO)
> + if (i == PCI_ROM_SLOT)
> + paddr = &pci_bios_mem_addr;
> + else if (val & PCI_ADDRESS_SPACE_IO)
> paddr = &pci_bios_io_addr;
> else if (size >= 0x04000000)
> paddr = &pci_bios_bigmem_addr;
> @@ -972,7 +977,24 @@ static void pci_bios_init_device(PCIDevice *d)
> paddr = &pci_bios_mem_addr;
> *paddr = (*paddr + size - 1) & ~(size - 1);
> pci_set_io_region_addr(d, i, *paddr);
> + BX_INFO("PCI: region %d: paddr 0x%x, size 0x%x\n",
> + i, *paddr, size);
> *paddr += size;
> +
> + /*
> + * Some complex PCI devices (such as graphic controllers)
> + * keep lots of internal state, including their PCI mappings
> + * that were determined at initial PCI configuration.
> + * To make them work under QEMU/KVM, we preserve the host's
> + * PCI mappings in the guest.
> + * (we have to do this twice to ensure we update the
> mappings)
> + */
>
Can you elaborate? Since we're running the card's bios again, won't it
initialize correctly?
Keeping the same mapping in the host and guest may not be possible in
some circumstances (differently sized pci holes, or using a 64-bit BAR
in a 64-bit host but running a 32-bit guest that cannot utilize 64-bit
BARs).
Other than that, the patch looks pretty good. It will need some
massaging before merging, but nothing serious.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 10:26 ` Avi Kivity
@ 2009-01-04 17:12 ` Leendert van Doorn
2009-01-04 17:28 ` Avi Kivity
0 siblings, 1 reply; 19+ messages in thread
From: Leendert van Doorn @ 2009-01-04 17:12 UTC (permalink / raw)
To: 'Avi Kivity'; +Cc: 'Shan, Haitao', 'Liu, Kechao', kvm
Avi wrote:
>This is worrying as it will cause us to diverge from upstream bochs bios.
Yep. I'll create a less invasive patch. I've been looking at SEABIOS which
seems a much better alternative but the GPLv3 license worries me, especially
when you have proprietary guests that calls back into it.
>> #if BX_ROMBIOS32
>> call rombios32_init
>> #else
>> @@ -10665,7 +10661,12 @@ post_default_ints:
>> call pcibios_init_iomem_bases
>> call pcibios_init_irqs
>> #endif //BX_PCIBIOS
>> -#endif
>> + mov cx, #0xc000 ;; init vga bios
>> + mov ax, #0xc780
>> + call rom_scan
>> +#endif //BX_ROMBIOS32
>> +
>> + call _print_bios_banner
>
>If one assigns a graphics card, it should replace the cirrus vga bios;
>that should make this change unnecessary.
You have to initialize the PCI subsystem before you can initialize the
VGABIOS. The PCI bus scan may have discovered the primary graphics adapter
and copied its BIOS to 0xC0000. By convention the primary VGA adapter ROM
always resides at 0xC0000.
I also have a patch for the cirrus-vga emulation so that it properly
emulates the PCI ROM BAR. I'll clean that up and submit it.
>> + /*
>> + * Some complex PCI devices (such as graphic controllers)
>> + * keep lots of internal state, including their PCI mappings
>> + * that were determined at initial PCI configuration.
>> + * To make them work under QEMU/KVM, we preserve the host's
>> + * PCI mappings in the guest.
>> + * (we have to do this twice to ensure we update the
>> mappings)
>> + */
>Can you elaborate? Since we're running the card's bios again, won't it
>initialize correctly?
The problem is that the device-assignment code doesn't update the cards
BARs, it just emulates them and maps the guest BARs onto the correct host
BARs. Unfortunately, the graphics card has undocumented interfaces for
obtaining the memory regions (faster than a full PCI lookup) and they of
course return the host mappings as opposed to the guest mappings.
My first attempt was to implement a state machine that would emulate these
interfaces and rewrite them but this got pretty ugly pretty fast. Ensuring
the same mappings was much cleaner.
Of course, my goal is to run unmodified BIOS/drivers. You could always
change the drivers.
>Keeping the same mapping in the host and guest may not be possible in
>some circumstances (differently sized pci holes, or using a 64-bit BAR
>in a 64-bit host but running a 32-bit guest that cannot utilize 64-bit
>BARs).
Yes but that all depends on what the host BIOS does. Most of my experiments
have been on a Dell optiplex 755 where all the PCI BARs are allocated at the
top of the 32-bit range (I run a 64-bit host and 32-bit guest). Arguably, my
rombios patch should do more validation than it does today.
>Other than that, the patch looks pretty good. It will need some
>massaging before merging, but nothing serious.
I'll clean things up and resubmit.
Leendert
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 17:12 ` Leendert van Doorn
@ 2009-01-04 17:28 ` Avi Kivity
2009-01-04 17:29 ` Avi Kivity
` (2 more replies)
0 siblings, 3 replies; 19+ messages in thread
From: Avi Kivity @ 2009-01-04 17:28 UTC (permalink / raw)
To: Leendert van Doorn, Kevin O'Connor
Cc: 'Shan, Haitao', 'Liu, Kechao', kvm
Leendert van Doorn wrote:
> Avi wrote:
>
>
>> This is worrying as it will cause us to diverge from upstream bochs bios.
>>
>
> Yep. I'll create a less invasive patch. I've been looking at SEABIOS which
> seems a much better alternative but the GPLv3 license worries me, especially
> when you have proprietary guests that calls back into it.
>
Surely, the BIOS call interface is a GPL boundary, just like the Linux
syscall interface. Kevin, is that not the case? If it is a GPL
interface, can you add an explicit exception to make it clear?
>> Can you elaborate? Since we're running the card's bios again, won't it
>> initialize correctly?
>>
>
> The problem is that the device-assignment code doesn't update the cards
> BARs, it just emulates them and maps the guest BARs onto the correct host
> BARs. Unfortunately, the graphics card has undocumented interfaces for
> obtaining the memory regions (faster than a full PCI lookup) and they of
> course return the host mappings as opposed to the guest mappings.
>
!@#$%^.
> My first attempt was to implement a state machine that would emulate these
> interfaces and rewrite them but this got pretty ugly pretty fast.
No doubt. And it wouldn't work for cards where we don't know about
these interfaces, or where we directly assign the mmio regions that
implement these interfaces.
> Ensuring
> the same mappings was much cleaner.
>
With a switch, please. Default behaviour should be to virtualize.
One way to implement it is to pass pci devfn -> BAR hints through the
firmware interface. This way you can choose which BARs to place where,
and where to allow the default placement.
> Of course, my goal is to run unmodified BIOS/drivers. You could always
> change the drivers.
>
>
Not Windows drivers.
> I'll clean things up and resubmit.
>
Thanks.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 17:28 ` Avi Kivity
@ 2009-01-04 17:29 ` Avi Kivity
2009-01-04 17:54 ` Leendert van Doorn
2009-01-04 18:03 ` Kevin O'Connor
2 siblings, 0 replies; 19+ messages in thread
From: Avi Kivity @ 2009-01-04 17:29 UTC (permalink / raw)
To: Leendert van Doorn, Kevin O'Connor
Cc: 'Shan, Haitao', 'Liu, Kechao', kvm
Avi Kivity wrote:
>
> Surely, the BIOS call interface is a GPL boundary, just like the Linux
> syscall interface. Kevin, is that not the case? If it is a GPL
> interface, can you add an explicit exception to make it clear?
s/GPL interface/GPL boundary/. Must be clear on this lawyer talk.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 17:28 ` Avi Kivity
2009-01-04 17:29 ` Avi Kivity
@ 2009-01-04 17:54 ` Leendert van Doorn
2009-01-04 19:51 ` Avi Kivity
2009-01-04 18:03 ` Kevin O'Connor
2 siblings, 1 reply; 19+ messages in thread
From: Leendert van Doorn @ 2009-01-04 17:54 UTC (permalink / raw)
To: 'Avi Kivity', 'Kevin O'Connor'
Cc: 'Shan, Haitao', 'Liu, Kechao', kvm
Avi wrote:
> Surely, the BIOS call interface is a GPL boundary, just like the Linux
> syscall interface. Kevin, is that not the case? If it is a GPL
> interface, can you add an explicit exception to make it clear?
At IBM we had many discussions about what precisely constitutes a GPL
boundary. Especially when you are examining internal BIOS data structures
(which most kernels do when accessing the BIOS). In the end Legal advised us
that LGPL was ok and GPL was not. This was all in the context of v2 so
things may have changed with v3.
I don't want this to turn into a GPL versus LGPL argument because I'm not a
lawyer either. If the KVM project picks up SEABIOS I'm all for it because it
is much cleaner and easier to maintain.
>> Ensuring
>> the same mappings was much cleaner.
> With a switch, please. Default behaviour should be to virtualize.
>
> One way to implement it is to pass pci devfn -> BAR hints through the
> firmware interface. This way you can choose which BARs to place where,
> and where to allow the default placement.
I'm already providing the hints, but you are proposing that the default
behavior for the BIOS should be to allocate BARs and when given a flag it
should try to preserve the host BAR mappings. I can do that.
>> Of course, my goal is to run unmodified BIOS/drivers. You could always
>> change the drivers.
>
> Not Windows drivers.
Well, at least I have a fighting chance to change the ATI windows drivers
:-)
Leendert
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 17:28 ` Avi Kivity
2009-01-04 17:29 ` Avi Kivity
2009-01-04 17:54 ` Leendert van Doorn
@ 2009-01-04 18:03 ` Kevin O'Connor
2 siblings, 0 replies; 19+ messages in thread
From: Kevin O'Connor @ 2009-01-04 18:03 UTC (permalink / raw)
To: Avi Kivity
Cc: Leendert van Doorn, 'Shan, Haitao', 'Liu, Kechao',
kvm
On Sun, Jan 04, 2009 at 07:28:50PM +0200, Avi Kivity wrote:
> Leendert van Doorn wrote:
>> Avi wrote:
>>> This is worrying as it will cause us to diverge from upstream bochs bios.
>> Yep. I'll create a less invasive patch. I've been looking at SEABIOS which
>> seems a much better alternative but the GPLv3 license worries me, especially
>> when you have proprietary guests that calls back into it.
> Surely, the BIOS call interface is a GPL boundary, just like the Linux
> syscall interface. Kevin, is that not the case? If it is a GPL
> interface, can you add an explicit exception to make it clear?
I would not want the SeaBIOS license to extend to an emulator that
deploys SeaBIOS nor to any regular real-mode apps that call SeaBIOS
handlers. I'd be happy to make this explicit.
-Kevin
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH][v2] kvm-userspace: Load PCI option ROMs
2009-01-04 17:54 ` Leendert van Doorn
@ 2009-01-04 19:51 ` Avi Kivity
0 siblings, 0 replies; 19+ messages in thread
From: Avi Kivity @ 2009-01-04 19:51 UTC (permalink / raw)
To: Leendert van Doorn
Cc: 'Kevin O'Connor', 'Shan, Haitao',
'Liu, Kechao', kvm
Leendert van Doorn wrote:
>> With a switch, please. Default behaviour should be to virtualize.
>>
>> One way to implement it is to pass pci devfn -> BAR hints through the
>> firmware interface. This way you can choose which BARs to place where,
>> and where to allow the default placement.
>>
>
> I'm already providing the hints, but you are proposing that the default
> behavior for the BIOS should be to allocate BARs and when given a flag it
> should try to preserve the host BAR mappings. I can do that.
>
It's a general policy -- isolate the guest from the host wherever
possible. In a few cases it isn't possible (time) and it really hurts.
>>> Of course, my goal is to run unmodified BIOS/drivers. You could always
>>> change the drivers.
>>>
>> Not Windows drivers.
>>
>
> Well, at least I have a fighting chance to change the ATI windows drivers
> :-)
>
:-)
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2009-01-04 19:51 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-12-29 3:51 [PATCH][v2] kvm-userspace: Load PCI option ROMs Liu, Kechao
2008-12-29 8:28 ` Avi Kivity
2008-12-29 9:36 ` Liu, Kechao
2008-12-29 9:49 ` Avi Kivity
2008-12-30 1:01 ` Shan, Haitao
2008-12-30 15:51 ` Avi Kivity
2008-12-31 2:06 ` Shan, Haitao
2008-12-31 9:28 ` Avi Kivity
2009-01-03 2:29 ` Shan, Haitao
2009-01-04 2:12 ` Shan, Haitao
2009-01-04 3:54 ` Leendert van Doorn
2009-01-04 4:58 ` Shan, Haitao
2009-01-04 10:26 ` Avi Kivity
2009-01-04 17:12 ` Leendert van Doorn
2009-01-04 17:28 ` Avi Kivity
2009-01-04 17:29 ` Avi Kivity
2009-01-04 17:54 ` Leendert van Doorn
2009-01-04 19:51 ` Avi Kivity
2009-01-04 18:03 ` Kevin O'Connor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox