mirror of https://github.com/xemu-project/xemu.git
pcnet: add loopback mode emulation
This patch enhances the pcnet NIC emulation with better loopback mode support, including CRC generation for looped-back packets in "raw" mode. The patch has practically no impact on the normal RX and TX path. Successfully tested against an ancient proprietary pcnet driver that does a lot of hardware checks on boot-up and now works fine over qemu as well. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5135 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
b319feb72d
commit
89b190a2bb
91
hw/pcnet.c
91
hw/pcnet.c
|
@ -35,6 +35,8 @@
|
||||||
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
|
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include "hw.h"
|
#include "hw.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
@ -52,6 +54,9 @@
|
||||||
#define PCNET_IOPORT_SIZE 0x20
|
#define PCNET_IOPORT_SIZE 0x20
|
||||||
#define PCNET_PNPMMIO_SIZE 0x20
|
#define PCNET_PNPMMIO_SIZE 0x20
|
||||||
|
|
||||||
|
#define PCNET_LOOPTEST_CRC 1
|
||||||
|
#define PCNET_LOOPTEST_NOCRC 2
|
||||||
|
|
||||||
|
|
||||||
typedef struct PCNetState_st PCNetState;
|
typedef struct PCNetState_st PCNetState;
|
||||||
|
|
||||||
|
@ -76,6 +81,7 @@ struct PCNetState_st {
|
||||||
void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr,
|
void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr,
|
||||||
uint8_t *buf, int len, int do_bswap);
|
uint8_t *buf, int len, int do_bswap);
|
||||||
void *dma_opaque;
|
void *dma_opaque;
|
||||||
|
int looptest;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qemu_ether_header {
|
struct qemu_ether_header {
|
||||||
|
@ -120,6 +126,7 @@ struct qemu_ether_header {
|
||||||
#define CSR_DRX(S) !!(((S)->csr[15])&0x0001)
|
#define CSR_DRX(S) !!(((S)->csr[15])&0x0001)
|
||||||
#define CSR_DTX(S) !!(((S)->csr[15])&0x0002)
|
#define CSR_DTX(S) !!(((S)->csr[15])&0x0002)
|
||||||
#define CSR_LOOP(S) !!(((S)->csr[15])&0x0004)
|
#define CSR_LOOP(S) !!(((S)->csr[15])&0x0004)
|
||||||
|
#define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008)
|
||||||
#define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000)
|
#define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000)
|
||||||
#define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000)
|
#define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000)
|
||||||
#define CSR_PROM(S) !!(((S)->csr[15])&0x8000)
|
#define CSR_PROM(S) !!(((S)->csr[15])&0x8000)
|
||||||
|
@ -202,6 +209,8 @@ struct pcnet_TMD {
|
||||||
#define TMDS_LTINT_SH 12
|
#define TMDS_LTINT_SH 12
|
||||||
#define TMDS_NOFCS_MASK 0x2000
|
#define TMDS_NOFCS_MASK 0x2000
|
||||||
#define TMDS_NOFCS_SH 13
|
#define TMDS_NOFCS_SH 13
|
||||||
|
#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
|
||||||
|
#define TMDS_ADDFCS_SH TMDS_NOFCS_SH
|
||||||
#define TMDS_ERR_MASK 0x4000
|
#define TMDS_ERR_MASK 0x4000
|
||||||
#define TMDS_ERR_SH 14
|
#define TMDS_ERR_SH 14
|
||||||
#define TMDS_OWN_MASK 0x8000
|
#define TMDS_OWN_MASK 0x8000
|
||||||
|
@ -1064,6 +1073,8 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
PCNetState *s = opaque;
|
PCNetState *s = opaque;
|
||||||
int is_padr = 0, is_bcast = 0, is_ladr = 0;
|
int is_padr = 0, is_bcast = 0, is_ladr = 0;
|
||||||
uint8_t buf1[60];
|
uint8_t buf1[60];
|
||||||
|
int remaining;
|
||||||
|
int crc_err = 0;
|
||||||
|
|
||||||
if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size)
|
if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size)
|
||||||
return;
|
return;
|
||||||
|
@ -1117,37 +1128,36 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
s->csr[0] |= 0x1000; /* Set MISS flag */
|
s->csr[0] |= 0x1000; /* Set MISS flag */
|
||||||
CSR_MISSC(s)++;
|
CSR_MISSC(s)++;
|
||||||
} else {
|
} else {
|
||||||
uint8_t *src = &s->buffer[8];
|
uint8_t *src = s->buffer;
|
||||||
target_phys_addr_t crda = CSR_CRDA(s);
|
target_phys_addr_t crda = CSR_CRDA(s);
|
||||||
struct pcnet_RMD rmd;
|
struct pcnet_RMD rmd;
|
||||||
int pktcount = 0;
|
int pktcount = 0;
|
||||||
|
|
||||||
memcpy(src, buf, size);
|
if (!s->looptest) {
|
||||||
|
memcpy(src, buf, size);
|
||||||
#if 1
|
/* no need to compute the CRC */
|
||||||
/* no need to compute the CRC */
|
src[size] = 0;
|
||||||
src[size] = 0;
|
src[size + 1] = 0;
|
||||||
src[size + 1] = 0;
|
src[size + 2] = 0;
|
||||||
src[size + 2] = 0;
|
src[size + 3] = 0;
|
||||||
src[size + 3] = 0;
|
size += 4;
|
||||||
size += 4;
|
} else if (s->looptest == PCNET_LOOPTEST_CRC ||
|
||||||
#else
|
!CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
|
||||||
/* XXX: avoid CRC generation */
|
|
||||||
if (!CSR_ASTRP_RCV(s)) {
|
|
||||||
uint32_t fcs = ~0;
|
uint32_t fcs = ~0;
|
||||||
uint8_t *p = src;
|
uint8_t *p = src;
|
||||||
|
|
||||||
while (size < 46) {
|
while (p != &src[size])
|
||||||
src[size++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (p != &src[size]) {
|
|
||||||
CRC(fcs, *p++);
|
CRC(fcs, *p++);
|
||||||
}
|
*(uint32_t *)p = htonl(fcs);
|
||||||
((uint32_t *)&src[size])[0] = htonl(fcs);
|
size += 4;
|
||||||
size += 4; /* FCS at end of packet */
|
} else {
|
||||||
} else size += 4;
|
uint32_t fcs = ~0;
|
||||||
#endif
|
uint8_t *p = src;
|
||||||
|
|
||||||
|
while (p != &src[size-4])
|
||||||
|
CRC(fcs, *p++);
|
||||||
|
crc_err = (*(uint32_t *)p != htonl(fcs));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef PCNET_DEBUG_MATCH
|
#ifdef PCNET_DEBUG_MATCH
|
||||||
PRINT_PKTHDR(buf);
|
PRINT_PKTHDR(buf);
|
||||||
|
@ -1158,24 +1168,30 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
SET_FIELD(&rmd.status, RMDS, STP, 1);
|
SET_FIELD(&rmd.status, RMDS, STP, 1);
|
||||||
|
|
||||||
#define PCNET_RECV_STORE() do { \
|
#define PCNET_RECV_STORE() do { \
|
||||||
int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),size); \
|
int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
|
||||||
target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \
|
target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \
|
||||||
s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
|
s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
|
||||||
src += count; size -= count; \
|
src += count; remaining -= count; \
|
||||||
SET_FIELD(&rmd.msg_length, RMDM, MCNT, count); \
|
|
||||||
SET_FIELD(&rmd.status, RMDS, OWN, 0); \
|
SET_FIELD(&rmd.status, RMDS, OWN, 0); \
|
||||||
RMDSTORE(&rmd, PHYSADDR(s,crda)); \
|
RMDSTORE(&rmd, PHYSADDR(s,crda)); \
|
||||||
pktcount++; \
|
pktcount++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
remaining = size;
|
||||||
PCNET_RECV_STORE();
|
PCNET_RECV_STORE();
|
||||||
if ((size > 0) && CSR_NRDA(s)) {
|
if ((remaining > 0) && CSR_NRDA(s)) {
|
||||||
target_phys_addr_t nrda = CSR_NRDA(s);
|
target_phys_addr_t nrda = CSR_NRDA(s);
|
||||||
|
#ifdef PCNET_DEBUG_RMD
|
||||||
|
PRINT_RMD(&rmd);
|
||||||
|
#endif
|
||||||
RMDLOAD(&rmd, PHYSADDR(s,nrda));
|
RMDLOAD(&rmd, PHYSADDR(s,nrda));
|
||||||
if (GET_FIELD(rmd.status, RMDS, OWN)) {
|
if (GET_FIELD(rmd.status, RMDS, OWN)) {
|
||||||
crda = nrda;
|
crda = nrda;
|
||||||
PCNET_RECV_STORE();
|
PCNET_RECV_STORE();
|
||||||
if ((size > 0) && (nrda=CSR_NNRD(s))) {
|
#ifdef PCNET_DEBUG_RMD
|
||||||
|
PRINT_RMD(&rmd);
|
||||||
|
#endif
|
||||||
|
if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
|
||||||
RMDLOAD(&rmd, PHYSADDR(s,nrda));
|
RMDLOAD(&rmd, PHYSADDR(s,nrda));
|
||||||
if (GET_FIELD(rmd.status, RMDS, OWN)) {
|
if (GET_FIELD(rmd.status, RMDS, OWN)) {
|
||||||
crda = nrda;
|
crda = nrda;
|
||||||
|
@ -1188,11 +1204,16 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
#undef PCNET_RECV_STORE
|
#undef PCNET_RECV_STORE
|
||||||
|
|
||||||
RMDLOAD(&rmd, PHYSADDR(s,crda));
|
RMDLOAD(&rmd, PHYSADDR(s,crda));
|
||||||
if (size == 0) {
|
if (remaining == 0) {
|
||||||
|
SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
|
||||||
SET_FIELD(&rmd.status, RMDS, ENP, 1);
|
SET_FIELD(&rmd.status, RMDS, ENP, 1);
|
||||||
SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
|
SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
|
||||||
SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
|
SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
|
||||||
SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
|
SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
|
||||||
|
if (crc_err) {
|
||||||
|
SET_FIELD(&rmd.status, RMDS, CRC, 1);
|
||||||
|
SET_FIELD(&rmd.status, RMDS, ERR, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SET_FIELD(&rmd.status, RMDS, OFLO, 1);
|
SET_FIELD(&rmd.status, RMDS, OFLO, 1);
|
||||||
SET_FIELD(&rmd.status, RMDS, BUFF, 1);
|
SET_FIELD(&rmd.status, RMDS, BUFF, 1);
|
||||||
|
@ -1229,6 +1250,8 @@ static void pcnet_transmit(PCNetState *s)
|
||||||
{
|
{
|
||||||
target_phys_addr_t xmit_cxda = 0;
|
target_phys_addr_t xmit_cxda = 0;
|
||||||
int count = CSR_XMTRL(s)-1;
|
int count = CSR_XMTRL(s)-1;
|
||||||
|
int add_crc = 0;
|
||||||
|
|
||||||
s->xmit_pos = -1;
|
s->xmit_pos = -1;
|
||||||
|
|
||||||
if (!CSR_TXON(s)) {
|
if (!CSR_TXON(s)) {
|
||||||
|
@ -1251,6 +1274,8 @@ static void pcnet_transmit(PCNetState *s)
|
||||||
if (GET_FIELD(tmd.status, TMDS, STP)) {
|
if (GET_FIELD(tmd.status, TMDS, STP)) {
|
||||||
s->xmit_pos = 0;
|
s->xmit_pos = 0;
|
||||||
xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
|
xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
|
||||||
|
if (BCR_SWSTYLE(s) != 1)
|
||||||
|
add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
|
||||||
}
|
}
|
||||||
if (!GET_FIELD(tmd.status, TMDS, ENP)) {
|
if (!GET_FIELD(tmd.status, TMDS, ENP)) {
|
||||||
int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
|
int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
|
||||||
|
@ -1265,9 +1290,13 @@ static void pcnet_transmit(PCNetState *s)
|
||||||
#ifdef PCNET_DEBUG
|
#ifdef PCNET_DEBUG
|
||||||
printf("pcnet_transmit size=%d\n", s->xmit_pos);
|
printf("pcnet_transmit size=%d\n", s->xmit_pos);
|
||||||
#endif
|
#endif
|
||||||
if (CSR_LOOP(s))
|
if (CSR_LOOP(s)) {
|
||||||
|
if (BCR_SWSTYLE(s) == 1)
|
||||||
|
add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
|
||||||
|
s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
|
||||||
pcnet_receive(s, s->buffer, s->xmit_pos);
|
pcnet_receive(s, s->buffer, s->xmit_pos);
|
||||||
else
|
s->looptest = 0;
|
||||||
|
} else
|
||||||
if (s->vc)
|
if (s->vc)
|
||||||
qemu_send_packet(s->vc, s->buffer, s->xmit_pos);
|
qemu_send_packet(s->vc, s->buffer, s->xmit_pos);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue