diff mbox

Write cmos hd data for ide drives using -device parm

Message ID 4BC054930200004800092ADD@sinclair.provo.novell.com
State New
Headers show

Commit Message

Bruce Rogers April 10, 2010, 4:36 p.m. UTC
When specifying ide devices using -device, the cmos information 
which the bios depends on is not written. This patch generalizes 
the cmos hd data setting for the existing code path and adds the 
ability to call that code on a per machine, per ide drive basis. 

This is important for older guests, such as Windows XP, and Windows 
Server 2003. 

I needed to add an id to the IDEBus structure since there wasn't 
a way to identify whether an ide bus was primary or secondary in 
the lower level code. 

Signed-off-by: Bruce Rogers <brogers@novell.com> 

Bruce 

 hw/ide/internal.h |    1  
 hw/ide/piix.c     |    2 + 
 hw/ide/qdev.c     |    9 ++++++- 
 hw/pc.c           |   68 ++++++++++++++++++++++++++++++++---------------------- 
 sysemu.h          |    1  
 vl.c              |    6 ++++ 
 6 files changed, 59 insertions(+), 28 deletions(-)
diff mbox

Patch

diff --git a/hw/ide/internal.h b/hw/ide/internal.h 
index 027029e..b04232c 100644 
--- a/hw/ide/internal.h 
+++ b/hw/ide/internal.h 
@@ -450,6 +450,7 @@  struct IDEBus { 
     IDEState ifs[2]; 
     uint8_t unit; 
     uint8_t cmd; 
+    uint8_t id; 
     qemu_irq irq; 
 }; 
  
diff --git a/hw/ide/piix.c b/hw/ide/piix.c 
index 295a93d..f62aadb 100644 
--- a/hw/ide/piix.c 
+++ b/hw/ide/piix.c 
@@ -136,6 +136,8 @@  static int pci_piix_ide_initfn(PCIIDEState *d) 
     ide_init_ioport(&d->bus[0], 0x1f0, 0x3f6); 
     ide_init_ioport(&d->bus[1], 0x170, 0x376); 
  
+    d->bus[0].id = 0; 
+    d->bus[1].id = 1; 
     ide_init2(&d->bus[0], NULL, NULL, isa_reserve_irq(14)); 
     ide_init2(&d->bus[1], NULL, NULL, isa_reserve_irq(15)); 
     return 0; 
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c 
index b18693d..632eccd 100644 
--- a/hw/ide/qdev.c 
+++ b/hw/ide/qdev.c 
@@ -39,6 +39,7 @@  static int ide_qdev_init(DeviceState *qdev, DeviceInfo *base) 
     IDEDevice *dev = DO_UPCAST(IDEDevice, qdev, qdev); 
     IDEDeviceInfo *info = DO_UPCAST(IDEDeviceInfo, qdev, base); 
     IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); 
+    int i; 
  
     if (!dev->conf.dinfo) { 
         fprintf(stderr, "%s: no drive specified\n", qdev->info->name); 
@@ -65,7 +66,13 @@  static int ide_qdev_init(DeviceState *qdev, DeviceInfo *base) 
     default: 
         goto err; 
     } 
-    return info->init(dev); 
+    dev->dinfo->bus = bus->id; 
+    dev->dinfo->unit = dev->unit; 
+    i = info->init(dev); 
+    if (i >= 0) { 
+        machine_ide_finalize(dev->dinfo); 
+    } 
+    return i; 
  
 err: 
     return -1; 
diff --git a/hw/pc.c b/hw/pc.c 
index 69e597f..29f47d4 100644 
--- a/hw/pc.c 
+++ b/hw/pc.c 
@@ -215,6 +215,44 @@  static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd) 
     rtc_set_memory(s, info_ofs + 8, sectors); 
 } 
  
+static void set_cmos_hd_data(DriveInfo *dinfo) 
+{ 
+    static int hd_type = 0; 
+    static int hd_trans = 0; 
+    RTCState *s = rtc_state; 
+    int cylinders, heads, sectors, translation; 
+ 
+    if (dinfo->bus == 0) { 
+        if (dinfo->unit == 0) { 
+            hd_type |= 0xf0; 
+            cmos_init_hd(0x19, 0x1b, dinfo->bdrv); 
+        } else { 
+            hd_type |= 0x0f; 
+            cmos_init_hd(0x1a, 0x24, dinfo->bdrv); 
+        } 
+        rtc_set_memory(s, 0x12, hd_type); 
+    } 
+    /* NOTE: bdrv_get_geometry_hint() returns the physical 
+        geometry.  It is always such that: 1 <= sects <= 63, 1 
+        <= heads <= 16, 1 <= cylinders <= 16383. The BIOS 
+        geometry can be different if a translation is done. */ 
+    translation = bdrv_get_translation_hint(dinfo->bdrv); 
+    if (translation == BIOS_ATA_TRANSLATION_AUTO) { 
+        bdrv_get_geometry_hint(dinfo->bdrv, &cylinders, &heads, &sectors); 
+        if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { 
+            /* No translation. */ 
+            translation = 0; 
+        } else { 
+            /* LBA translation. */ 
+            translation = 1; 
+        } 
+    } else { 
+        translation--; 
+    } 
+    hd_trans |= translation << ((dinfo->bus * 4) + (dinfo->unit * 2)); 
+    rtc_set_memory(s, 0x39, hd_trans); 
+} 
+ 
 /* convert boot_device letter to something recognizable by the bios */ 
 static int boot_device2nibble(char boot_device) 
 { 
@@ -338,37 +376,11 @@  static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, 
  
     /* hard drives */ 
  
-    rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); 
-    if (hd_table[0]) 
-        cmos_init_hd(0x19, 0x1b, hd_table[0]->bdrv); 
-    if (hd_table[1]) 
-        cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv); 
- 
-    val = 0; 
     for (i = 0; i < 4; i++) { 
         if (hd_table[i]) { 
-            int cylinders, heads, sectors, translation; 
-            /* NOTE: bdrv_get_geometry_hint() returns the physical 
-                geometry.  It is always such that: 1 <= sects <= 63, 1 
-                <= heads <= 16, 1 <= cylinders <= 16383. The BIOS 
-                geometry can be different if a translation is done. */ 
-            translation = bdrv_get_translation_hint(hd_table[i]->bdrv); 
-            if (translation == BIOS_ATA_TRANSLATION_AUTO) { 
-                bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, &sectors); 
-                if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { 
-                    /* No translation. */ 
-                    translation = 0; 
-                } else { 
-                    /* LBA translation. */ 
-                    translation = 1; 
-                } 
-            } else { 
-                translation--; 
-            } 
-            val |= translation << (i * 2); 
+            set_cmos_hd_data(hd_table[i]); 
         } 
     } 
-    rtc_set_memory(s, 0x39, val); 
 } 
  
 void ioport_set_a20(int enable) 
@@ -820,6 +832,8 @@  static void pc_init1(ram_addr_t ram_size, 
     DriveInfo *fd[MAX_FD]; 
     void *fw_cfg; 
  
+    machine_ide_finalize = set_cmos_hd_data; 
+ 
     if (ram_size >= 0xe0000000 ) { 
         above_4g_mem_size = ram_size - 0xe0000000; 
         below_4g_mem_size = 0xe0000000; 
diff --git a/sysemu.h b/sysemu.h 
index d0effa0..564d477 100644 
--- a/sysemu.h 
+++ b/sysemu.h 
@@ -242,5 +242,6 @@  void usb_info(Monitor *mon); 
 void rtc_change_mon_event(struct tm *tm); 
  
 void register_devices(void); 
+extern void (*machine_ide_finalize)(DriveInfo *dinfo); 
  
 #endif 
diff --git a/vl.c b/vl.c 
index ea79ac4..8ab3349 100644 
--- a/vl.c 
+++ b/vl.c 
@@ -228,6 +228,7 @@  int ctrl_grab = 0; 
 unsigned int nb_prom_envs = 0; 
 const char *prom_envs[MAX_PROM_ENVS]; 
 int boot_menu; 
+void (*machine_ide_finalize)(DriveInfo *dinfo); 
  
 int nb_numa_nodes; 
 uint64_t node_mem[MAX_NODES]; 
@@ -275,6 +276,10 @@  static struct { 
     { .driver = "vmware-svga",          .flag = &default_vga       }, 
 }; 
  
+static void default_ide_finalize(DriveInfo *dinfo) 
+{ 
+} 
+ 
 static int default_driver_check(QemuOpts *opts, void *opaque) 
 { 
     const char *driver = qemu_opt_get(opts, "driver"); 
@@ -2624,6 +2629,7 @@  int main(int argc, char **argv, char **envp) 
  
     module_call_init(MODULE_INIT_MACHINE); 
     machine = find_default_machine(); 
+    machine_ide_finalize = default_ide_finalize; 
     cpu_model = NULL; 
     initrd_filename = NULL; 
     ram_size = 0;