From af966e1a5e54afee02dfd4a5086b2298a6a1c96a Mon Sep 17 00:00:00 2001
From: Bruce Rogers <brogers@suse.com>
Date: Mon, 22 Jan 2018 14:57:35 -0700
Subject: [PATCH] kvm-qemu-rtl8139-link

---
 hw/pc.c      | 10 +++++++---
 hw/qdev.c    |  1 +
 hw/rtl8139.c | 25 +++++++++++++++++++++++--
 net.c        |  1 +
 net.h        | 10 ++++++++++
 5 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/hw/pc.c b/hw/pc.c
index 7d68845e0a..e016f8decf 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1223,10 +1223,14 @@ static void pc_init1(ram_addr_t ram_size,
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 
-        if (!(model > MODEL_ISA) || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
+        if (!(model > MODEL_ISA) || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
             pc_init_ne2k_isa(nd);
-        else
+        } else if (model == MODEL_MAC) {
+            nd->link = Link_10mbps;
+            pci_nic_init_nofail(nd, "rtl8139", NULL);
+        } else {
             pci_nic_init_nofail(nd, "rtl8139", NULL);
+        }
     }
 
     if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
@@ -1240,7 +1244,7 @@ static void pc_init1(ram_addr_t ram_size,
 
     if (model == MODEL_MAC) {
         pci_ich6_ide_init(pci_bus, hd, piix3_devfn + 1);
-    else if (model > MODEL_ISA) {
+    } else if (model > MODEL_ISA) {
         pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
     } else {
         for(i = 0; i < MAX_IDE_BUS; i++) {
diff --git a/hw/qdev.c b/hw/qdev.c
index b6bd4aeabd..02643bfa30 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -372,6 +372,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
         qdev_prop_exists(dev, "vectors")) {
         qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
     }
+    qdev_prop_set_uint32(dev, "link", nd->link);
 }
 
 static int next_block_unit[IF_COUNT];
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index fcdcd1d557..50dfaebe04 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -417,6 +417,9 @@ static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
 /* Writes tally counters to specified physical memory address */
 static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* counters);
 
+static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr);
+static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr);
+
 typedef struct RTL8139State {
     PCIDevice dev;
     uint8_t phys[8]; /* mac address */
@@ -458,6 +461,7 @@ typedef struct RTL8139State {
 
     uint16_t CpCmd;
     uint8_t  TxThresh;
+    enum NICLink link;
 
     NICState *nic;
     NICConf conf;
@@ -1228,7 +1232,7 @@ static void rtl8139_reset(DeviceState *d)
     s->Config0 = 0x0; /* No boot ROM */
     s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
     s->Config3 = 0x1; /* fast back-to-back compatible */
-    s->Config5 = 0x0;
+    s->Config5 = Cfg5_LDPS;
 
     s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
 
@@ -1254,6 +1258,13 @@ static void rtl8139_reset(DeviceState *d)
     s->TimerInt = 0;
     s->TCTR_base = 0;
 
+    s->eeprom.contents[10] = s->Config0 | s->Config1 << 8;
+    s->eeprom.contents[6] = (rtl8139_io_readb(s, MediaStatus) & 0xc0) | ((rtl8139_io_readw(s, BasicModeCtrl) >> 8) & 0x23)
+                          | (s->Config3 << 8);
+    s->eeprom.contents[12] = s->Config4 << 8;
+
+    s->eeprom.contents[15] = s->Config5 << 8;
+
     /* reset tally counters */
     RTL8139TallyCounters_clear(&s->tally_counters);
 }
@@ -2839,7 +2850,7 @@ static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
             break;
 
         case MediaStatus:
-            ret = 0xd0;
+            ret = 0xd0 | ((s->link == Link_10mbps) << 3);
             DEBUG_PRINT(("RTL8139: MediaStatus read 0x%x\n", ret));
             break;
 
@@ -3339,6 +3350,16 @@ static int pci_rtl8139_init(PCIDevice *dev)
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
+     switch(s->conf.link) {
+         case Link_10mbps:
+         case Link_100mbps:
+             s->link = s->conf.link;
+             break;
+         default:
+             s->link = Link_100mbps;
+             break;
+     }
+
     s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
                           dev->qdev.info->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
diff --git a/net.c b/net.c
index 28b6b43a94..04627449d1 100644
--- a/net.c
+++ b/net.c
@@ -812,6 +812,7 @@ static int net_init_nic(QemuOpts *opts,
     }
 
     nd->used = 1;
+    nd->link = Link_default;
     nb_nics++;
 
     return idx;
diff --git a/net.h b/net.h
index 209431dd04..c1fd9b5653 100644
--- a/net.h
+++ b/net.h
@@ -13,15 +13,24 @@ struct MACAddr {
 
 /* qdev nic properties */
 
+enum NICLink {
+    Link_default,
+    Link_10mbps,
+    Link_100mbps,
+    Link_1000mbps,
+};
+
 typedef struct NICConf {
     MACAddr macaddr;
     VLANState *vlan;
     VLANClientState *peer;
+    uint32_t link;
 } NICConf;
 
 #define DEFINE_NIC_PROPERTIES(_state, _conf)                            \
     DEFINE_PROP_MACADDR("mac",   _state, _conf.macaddr),                \
     DEFINE_PROP_VLAN("vlan",     _state, _conf.vlan),                   \
+    DEFINE_PROP_UINT32("link",     _state, _conf.link, Link_default),   \
     DEFINE_PROP_NETDEV("netdev", _state, _conf.peer)
 
 /* VLANs support */
@@ -132,6 +141,7 @@ struct NICInfo {
     VLANState *vlan;
     VLANClientState *netdev;
     int used;
+    enum NICLink link;
     int bootable;
     int nvectors;
 };
