From 54e1122d6ef29c0ecdb325a05717a695fa96b5ef Mon Sep 17 00:00:00 2001
From: Bruce Rogers <brogers@suse.com>
Date: Mon, 22 Jan 2018 15:04:05 -0700
Subject: [PATCH] kvm-qemu-preXX-qdev-ide-bios-setup

---
 hw/ide/internal.h |  3 ++-
 hw/ide/isa.c      |  8 ++++++-
 hw/ide/piix.c     |  6 +++--
 hw/ide/qdev.c     | 13 ++++++++---
 hw/pc.c           | 68 +++++++++++++++++++++++++++++++++----------------------
 sysemu.h          |  1 +
 vl.c              |  6 +++++
 7 files changed, 71 insertions(+), 34 deletions(-)

diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 8615d1a1fa..6dfd2ff4ae 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -448,6 +448,7 @@ struct IDEBus {
     IDEState ifs[2];
     uint8_t unit;
     uint8_t cmd;
+    uint8_t id;
     qemu_irq irq;
 };
 
@@ -562,7 +563,7 @@ void ide_init2(IDEBus *bus, DriveInfo *hd0, DriveInfo *hd1,
 void ide_init_ioport(IDEBus *bus, int iobase, int iobase2);
 
 /* hw/ide/qdev.c */
-void ide_bus_new(IDEBus *idebus, DeviceState *dev);
+void ide_bus_new(IDEBus *idebus, DeviceState *dev, const char *name);
 IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
 
 #endif /* HW_IDE_INTERNAL_H */
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
index dff7c796f7..ac3ee3b703 100644
--- a/hw/ide/isa.c
+++ b/hw/ide/isa.c
@@ -67,7 +67,13 @@ static int isa_ide_initfn(ISADevice *dev)
 {
     ISAIDEState *s = DO_UPCAST(ISAIDEState, dev, dev);
 
-    ide_bus_new(&s->bus, &s->dev.qdev);
+    if (s->iobase == 0x1f0) {
+        ide_bus_new(&s->bus, &s->dev.qdev, "ide.0");
+        s->bus.id = 0;
+    } else {
+        ide_bus_new(&s->bus, &s->dev.qdev, "ide.1");
+        s->bus.id = 1;
+    }
     ide_init_ioport(&s->bus, s->iobase, s->iobase2);
     isa_init_irq(dev, &s->irq, s->isairq);
     ide_init2(&s->bus, NULL, NULL, s->irq);
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index e18c2ec4bb..c486ed9922 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -128,11 +128,13 @@ static int pci_piix_ide_initfn(PCIIDEState *d)
 
     vmstate_register(0, &vmstate_ide_pci, d);
 
-    ide_bus_new(&d->bus[0], &d->dev.qdev);
-    ide_bus_new(&d->bus[1], &d->dev.qdev);
+    ide_bus_new(&d->bus[0], &d->dev.qdev, NULL);
+    ide_bus_new(&d->bus[1], &d->dev.qdev, NULL);
     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 0b84a4f1d0..699c0cafed 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -29,9 +29,9 @@ static struct BusInfo ide_bus_info = {
     .size  = sizeof(IDEBus),
 };
 
-void ide_bus_new(IDEBus *idebus, DeviceState *dev)
+void ide_bus_new(IDEBus *idebus, DeviceState *dev, const char *name)
 {
-    qbus_create_inplace(&idebus->qbus, &ide_bus_info, dev, NULL);
+    qbus_create_inplace(&idebus->qbus, &ide_bus_info, dev, name);
 }
 
 static int ide_qdev_init(DeviceState *qdev, DeviceInfo *base)
@@ -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->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 e016f8decf..3b55dd6c89 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -212,6 +212,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)
 {
@@ -348,37 +386,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)
@@ -1016,6 +1028,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 a545a2b8af..4baf2571b3 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -265,5 +265,6 @@ void do_usb_del(Monitor *mon, const QDict *qdict);
 void usb_info(Monitor *mon);
 
 void register_devices(void);
+extern void (*machine_ide_finalize)(DriveInfo *dinfo);
 
 #endif
diff --git a/vl.c b/vl.c
index a1f6823542..8e878aaaca 100644
--- a/vl.c
+++ b/vl.c
@@ -260,6 +260,7 @@ const char *prom_envs[MAX_PROM_ENVS];
 #endif
 const char *nvram = NULL;
 int boot_menu;
+void (*machine_ide_finalize)(DriveInfo *dinfo);
 
 int nb_numa_nodes;
 uint64_t node_mem[MAX_NODES];
@@ -307,6 +308,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");
@@ -4977,6 +4982,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;
