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 472 uint32_t currTxDesc;
473 473  
474 474 /* C+ mode */
  475 + uint32_t cplus_enabled;
  476 +
475 477 uint32_t currCPlusRxDesc;
476 478 uint32_t currCPlusTxDesc;
477 479  
... ... @@ -1232,6 +1234,8 @@ static void rtl8139_reset(RTL8139State *s)
1232 1234 s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
1233 1235  
1234 1236 s->CpCmd = 0x0; /* reset C+ mode */
  1237 + s->cplus_enabled = 0;
  1238 +
1235 1239  
1236 1240 // s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
1237 1241 // s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
... ... @@ -1420,6 +1424,8 @@ static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
1420 1424  
1421 1425 DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val));
1422 1426  
  1427 + s->cplus_enabled = 1;
  1428 +
1423 1429 /* mask unwriteable bits */
1424 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 2373  
2368 2374 /* handle C+ transmit mode register configuration */
2369 2375  
2370   - if (rtl8139_cp_transmitter_enabled(s))
  2376 + if (s->cplus_enabled)
2371 2377 {
2372 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 3196 qemu_put_be64(f, s->TCTR_base);
3191 3197  
3192 3198 RTL8139TallyCounters_save(f, &s->tally_counters);
  3199 +
  3200 + qemu_put_be32s(f, &s->cplus_enabled);
3193 3201 }
3194 3202  
3195 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 3207 int ret;
3200 3208  
3201 3209 /* just 2 versions for now */
3202   - if (version_id > 3)
  3210 + if (version_id > 4)
3203 3211 return -EINVAL;
3204 3212  
3205 3213 if (version_id >= 3) {
... ... @@ -3299,6 +3307,12 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id)
3299 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 3316 return 0;
3303 3317 }
3304 3318  
... ... @@ -3447,7 +3461,7 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
3447 3461 s->cplus_txbuffer_len = 0;
3448 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 3466 #ifdef RTL8139_ONBOARD_TIMER
3453 3467 s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
... ...