Commit 2c3891ab7feb9f1ed3e09f405866585d89efa243

Authored by aliguori
1 parent f094a782

RTL8139: Latch C+ mode state instead of inferring it from C+ Command register (Avi Kivity)

It was observed that Windows 2003 x64 hangs when shutting down if an
RTL8139 NIC and a USB device tablet are both present.  What seems to be
happening is:

- the guest shuts down the transmitter and receiver
- time passes
- the guest requests a tally counter dump

As it happens, the tally counter command register overlaps the transmit
status register in C mode.  Qemu determines whether the chip is in C or C+
mode by looking at the C+ transmit enable bit; as this is now unset, the
dump tally counter command is interpreted as a C mode transmit command.  The
guest doesn't think so, however, and continues to poll for completion of the
tally counter dump command.  This never occurs, so the guest hangs.

Fix by redefining C+ mode as "a write to the C+ command register has occurred
since the last reset".  The data sheet is silent on the matter.

Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6279 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 17 additions and 3 deletions
hw/rtl8139.c
@@ -472,6 +472,8 @@ typedef struct RTL8139State { @@ -472,6 +472,8 @@ typedef struct RTL8139State {
472 uint32_t currTxDesc; 472 uint32_t currTxDesc;
473 473
474 /* C+ mode */ 474 /* C+ mode */
  475 + uint32_t cplus_enabled;
  476 +
475 uint32_t currCPlusRxDesc; 477 uint32_t currCPlusRxDesc;
476 uint32_t currCPlusTxDesc; 478 uint32_t currCPlusTxDesc;
477 479
@@ -1232,6 +1234,8 @@ static void rtl8139_reset(RTL8139State *s) @@ -1232,6 +1234,8 @@ static void rtl8139_reset(RTL8139State *s)
1232 s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; 1234 s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
1233 1235
1234 s->CpCmd = 0x0; /* reset C+ mode */ 1236 s->CpCmd = 0x0; /* reset C+ mode */
  1237 + s->cplus_enabled = 0;
  1238 +
1235 1239
1236 // s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation 1240 // s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
1237 // s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex 1241 // s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
@@ -1420,6 +1424,8 @@ static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val) @@ -1420,6 +1424,8 @@ static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
1420 1424
1421 DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val)); 1425 DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val));
1422 1426
  1427 + s->cplus_enabled = 1;
  1428 +
1423 /* mask unwriteable bits */ 1429 /* mask unwriteable bits */
1424 val = SET_MASKED(val, 0xff84, s->CpCmd); 1430 val = SET_MASKED(val, 0xff84, s->CpCmd);
1425 1431
@@ -2367,7 +2373,7 @@ static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32 @@ -2367,7 +2373,7 @@ static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32
2367 2373
2368 /* handle C+ transmit mode register configuration */ 2374 /* handle C+ transmit mode register configuration */
2369 2375
2370 - if (rtl8139_cp_transmitter_enabled(s)) 2376 + if (s->cplus_enabled)
2371 { 2377 {
2372 DEBUG_PRINT(("RTL8139C+ DTCCR write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor)); 2378 DEBUG_PRINT(("RTL8139C+ DTCCR write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor));
2373 2379
@@ -3190,6 +3196,8 @@ static void rtl8139_save(QEMUFile* f,void* opaque) @@ -3190,6 +3196,8 @@ static void rtl8139_save(QEMUFile* f,void* opaque)
3190 qemu_put_be64(f, s->TCTR_base); 3196 qemu_put_be64(f, s->TCTR_base);
3191 3197
3192 RTL8139TallyCounters_save(f, &s->tally_counters); 3198 RTL8139TallyCounters_save(f, &s->tally_counters);
  3199 +
  3200 + qemu_put_be32s(f, &s->cplus_enabled);
3193 } 3201 }
3194 3202
3195 static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) 3203 static int rtl8139_load(QEMUFile* f,void* opaque,int version_id)
@@ -3199,7 +3207,7 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) @@ -3199,7 +3207,7 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id)
3199 int ret; 3207 int ret;
3200 3208
3201 /* just 2 versions for now */ 3209 /* just 2 versions for now */
3202 - if (version_id > 3) 3210 + if (version_id > 4)
3203 return -EINVAL; 3211 return -EINVAL;
3204 3212
3205 if (version_id >= 3) { 3213 if (version_id >= 3) {
@@ -3299,6 +3307,12 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) @@ -3299,6 +3307,12 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id)
3299 RTL8139TallyCounters_clear(&s->tally_counters); 3307 RTL8139TallyCounters_clear(&s->tally_counters);
3300 } 3308 }
3301 3309
  3310 + if (version_id >= 4) {
  3311 + qemu_get_be32s(f, &s->cplus_enabled);
  3312 + } else {
  3313 + s->cplus_enabled = s->CpCmd != 0;
  3314 + }
  3315 +
3302 return 0; 3316 return 0;
3303 } 3317 }
3304 3318
@@ -3447,7 +3461,7 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn) @@ -3447,7 +3461,7 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
3447 s->cplus_txbuffer_len = 0; 3461 s->cplus_txbuffer_len = 0;
3448 s->cplus_txbuffer_offset = 0; 3462 s->cplus_txbuffer_offset = 0;
3449 3463
3450 - register_savevm("rtl8139", -1, 3, rtl8139_save, rtl8139_load, s); 3464 + register_savevm("rtl8139", -1, 4, rtl8139_save, rtl8139_load, s);
3451 3465
3452 #ifdef RTL8139_ONBOARD_TIMER 3466 #ifdef RTL8139_ONBOARD_TIMER
3453 s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s); 3467 s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);