Commit 89b190a2bb82b1226b5cc05846e9a063c0d0efa3
1 parent
b319feb7
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
Showing
1 changed file
with
59 additions
and
30 deletions
hw/pcnet.c
@@ -35,6 +35,8 @@ | @@ -35,6 +35,8 @@ | ||
35 | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt | 35 | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt |
36 | */ | 36 | */ |
37 | 37 | ||
38 | +#include <netinet/in.h> | ||
39 | + | ||
38 | #include "hw.h" | 40 | #include "hw.h" |
39 | #include "pci.h" | 41 | #include "pci.h" |
40 | #include "net.h" | 42 | #include "net.h" |
@@ -52,6 +54,9 @@ | @@ -52,6 +54,9 @@ | ||
52 | #define PCNET_IOPORT_SIZE 0x20 | 54 | #define PCNET_IOPORT_SIZE 0x20 |
53 | #define PCNET_PNPMMIO_SIZE 0x20 | 55 | #define PCNET_PNPMMIO_SIZE 0x20 |
54 | 56 | ||
57 | +#define PCNET_LOOPTEST_CRC 1 | ||
58 | +#define PCNET_LOOPTEST_NOCRC 2 | ||
59 | + | ||
55 | 60 | ||
56 | typedef struct PCNetState_st PCNetState; | 61 | typedef struct PCNetState_st PCNetState; |
57 | 62 | ||
@@ -76,6 +81,7 @@ struct PCNetState_st { | @@ -76,6 +81,7 @@ struct PCNetState_st { | ||
76 | void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr, | 81 | void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr, |
77 | uint8_t *buf, int len, int do_bswap); | 82 | uint8_t *buf, int len, int do_bswap); |
78 | void *dma_opaque; | 83 | void *dma_opaque; |
84 | + int looptest; | ||
79 | }; | 85 | }; |
80 | 86 | ||
81 | struct qemu_ether_header { | 87 | struct qemu_ether_header { |
@@ -120,6 +126,7 @@ struct qemu_ether_header { | @@ -120,6 +126,7 @@ struct qemu_ether_header { | ||
120 | #define CSR_DRX(S) !!(((S)->csr[15])&0x0001) | 126 | #define CSR_DRX(S) !!(((S)->csr[15])&0x0001) |
121 | #define CSR_DTX(S) !!(((S)->csr[15])&0x0002) | 127 | #define CSR_DTX(S) !!(((S)->csr[15])&0x0002) |
122 | #define CSR_LOOP(S) !!(((S)->csr[15])&0x0004) | 128 | #define CSR_LOOP(S) !!(((S)->csr[15])&0x0004) |
129 | +#define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008) | ||
123 | #define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000) | 130 | #define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000) |
124 | #define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000) | 131 | #define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000) |
125 | #define CSR_PROM(S) !!(((S)->csr[15])&0x8000) | 132 | #define CSR_PROM(S) !!(((S)->csr[15])&0x8000) |
@@ -202,6 +209,8 @@ struct pcnet_TMD { | @@ -202,6 +209,8 @@ struct pcnet_TMD { | ||
202 | #define TMDS_LTINT_SH 12 | 209 | #define TMDS_LTINT_SH 12 |
203 | #define TMDS_NOFCS_MASK 0x2000 | 210 | #define TMDS_NOFCS_MASK 0x2000 |
204 | #define TMDS_NOFCS_SH 13 | 211 | #define TMDS_NOFCS_SH 13 |
212 | +#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK | ||
213 | +#define TMDS_ADDFCS_SH TMDS_NOFCS_SH | ||
205 | #define TMDS_ERR_MASK 0x4000 | 214 | #define TMDS_ERR_MASK 0x4000 |
206 | #define TMDS_ERR_SH 14 | 215 | #define TMDS_ERR_SH 14 |
207 | #define TMDS_OWN_MASK 0x8000 | 216 | #define TMDS_OWN_MASK 0x8000 |
@@ -1064,6 +1073,8 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | @@ -1064,6 +1073,8 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | ||
1064 | PCNetState *s = opaque; | 1073 | PCNetState *s = opaque; |
1065 | int is_padr = 0, is_bcast = 0, is_ladr = 0; | 1074 | int is_padr = 0, is_bcast = 0, is_ladr = 0; |
1066 | uint8_t buf1[60]; | 1075 | uint8_t buf1[60]; |
1076 | + int remaining; | ||
1077 | + int crc_err = 0; | ||
1067 | 1078 | ||
1068 | if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size) | 1079 | if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size) |
1069 | return; | 1080 | return; |
@@ -1117,37 +1128,36 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | @@ -1117,37 +1128,36 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | ||
1117 | s->csr[0] |= 0x1000; /* Set MISS flag */ | 1128 | s->csr[0] |= 0x1000; /* Set MISS flag */ |
1118 | CSR_MISSC(s)++; | 1129 | CSR_MISSC(s)++; |
1119 | } else { | 1130 | } else { |
1120 | - uint8_t *src = &s->buffer[8]; | 1131 | + uint8_t *src = s->buffer; |
1121 | target_phys_addr_t crda = CSR_CRDA(s); | 1132 | target_phys_addr_t crda = CSR_CRDA(s); |
1122 | struct pcnet_RMD rmd; | 1133 | struct pcnet_RMD rmd; |
1123 | int pktcount = 0; | 1134 | int pktcount = 0; |
1124 | 1135 | ||
1125 | - memcpy(src, buf, size); | ||
1126 | - | ||
1127 | -#if 1 | ||
1128 | - /* no need to compute the CRC */ | ||
1129 | - src[size] = 0; | ||
1130 | - src[size + 1] = 0; | ||
1131 | - src[size + 2] = 0; | ||
1132 | - src[size + 3] = 0; | ||
1133 | - size += 4; | ||
1134 | -#else | ||
1135 | - /* XXX: avoid CRC generation */ | ||
1136 | - if (!CSR_ASTRP_RCV(s)) { | 1136 | + if (!s->looptest) { |
1137 | + memcpy(src, buf, size); | ||
1138 | + /* no need to compute the CRC */ | ||
1139 | + src[size] = 0; | ||
1140 | + src[size + 1] = 0; | ||
1141 | + src[size + 2] = 0; | ||
1142 | + src[size + 3] = 0; | ||
1143 | + size += 4; | ||
1144 | + } else if (s->looptest == PCNET_LOOPTEST_CRC || | ||
1145 | + !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) { | ||
1137 | uint32_t fcs = ~0; | 1146 | uint32_t fcs = ~0; |
1138 | uint8_t *p = src; | 1147 | uint8_t *p = src; |
1139 | 1148 | ||
1140 | - while (size < 46) { | ||
1141 | - src[size++] = 0; | ||
1142 | - } | 1149 | + while (p != &src[size]) |
1150 | + CRC(fcs, *p++); | ||
1151 | + *(uint32_t *)p = htonl(fcs); | ||
1152 | + size += 4; | ||
1153 | + } else { | ||
1154 | + uint32_t fcs = ~0; | ||
1155 | + uint8_t *p = src; | ||
1143 | 1156 | ||
1144 | - while (p != &src[size]) { | 1157 | + while (p != &src[size-4]) |
1145 | CRC(fcs, *p++); | 1158 | CRC(fcs, *p++); |
1146 | - } | ||
1147 | - ((uint32_t *)&src[size])[0] = htonl(fcs); | ||
1148 | - size += 4; /* FCS at end of packet */ | ||
1149 | - } else size += 4; | ||
1150 | -#endif | 1159 | + crc_err = (*(uint32_t *)p != htonl(fcs)); |
1160 | + } | ||
1151 | 1161 | ||
1152 | #ifdef PCNET_DEBUG_MATCH | 1162 | #ifdef PCNET_DEBUG_MATCH |
1153 | PRINT_PKTHDR(buf); | 1163 | PRINT_PKTHDR(buf); |
@@ -1158,24 +1168,30 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | @@ -1158,24 +1168,30 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | ||
1158 | SET_FIELD(&rmd.status, RMDS, STP, 1); | 1168 | SET_FIELD(&rmd.status, RMDS, STP, 1); |
1159 | 1169 | ||
1160 | #define PCNET_RECV_STORE() do { \ | 1170 | #define PCNET_RECV_STORE() do { \ |
1161 | - int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),size); \ | 1171 | + int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \ |
1162 | target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \ | 1172 | target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \ |
1163 | s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \ | 1173 | s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \ |
1164 | - src += count; size -= count; \ | ||
1165 | - SET_FIELD(&rmd.msg_length, RMDM, MCNT, count); \ | 1174 | + src += count; remaining -= count; \ |
1166 | SET_FIELD(&rmd.status, RMDS, OWN, 0); \ | 1175 | SET_FIELD(&rmd.status, RMDS, OWN, 0); \ |
1167 | RMDSTORE(&rmd, PHYSADDR(s,crda)); \ | 1176 | RMDSTORE(&rmd, PHYSADDR(s,crda)); \ |
1168 | pktcount++; \ | 1177 | pktcount++; \ |
1169 | } while (0) | 1178 | } while (0) |
1170 | 1179 | ||
1180 | + remaining = size; | ||
1171 | PCNET_RECV_STORE(); | 1181 | PCNET_RECV_STORE(); |
1172 | - if ((size > 0) && CSR_NRDA(s)) { | 1182 | + if ((remaining > 0) && CSR_NRDA(s)) { |
1173 | target_phys_addr_t nrda = CSR_NRDA(s); | 1183 | target_phys_addr_t nrda = CSR_NRDA(s); |
1184 | +#ifdef PCNET_DEBUG_RMD | ||
1185 | + PRINT_RMD(&rmd); | ||
1186 | +#endif | ||
1174 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); | 1187 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); |
1175 | if (GET_FIELD(rmd.status, RMDS, OWN)) { | 1188 | if (GET_FIELD(rmd.status, RMDS, OWN)) { |
1176 | crda = nrda; | 1189 | crda = nrda; |
1177 | PCNET_RECV_STORE(); | 1190 | PCNET_RECV_STORE(); |
1178 | - if ((size > 0) && (nrda=CSR_NNRD(s))) { | 1191 | +#ifdef PCNET_DEBUG_RMD |
1192 | + PRINT_RMD(&rmd); | ||
1193 | +#endif | ||
1194 | + if ((remaining > 0) && (nrda=CSR_NNRD(s))) { | ||
1179 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); | 1195 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); |
1180 | if (GET_FIELD(rmd.status, RMDS, OWN)) { | 1196 | if (GET_FIELD(rmd.status, RMDS, OWN)) { |
1181 | crda = nrda; | 1197 | crda = nrda; |
@@ -1188,11 +1204,16 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | @@ -1188,11 +1204,16 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) | ||
1188 | #undef PCNET_RECV_STORE | 1204 | #undef PCNET_RECV_STORE |
1189 | 1205 | ||
1190 | RMDLOAD(&rmd, PHYSADDR(s,crda)); | 1206 | RMDLOAD(&rmd, PHYSADDR(s,crda)); |
1191 | - if (size == 0) { | 1207 | + if (remaining == 0) { |
1208 | + SET_FIELD(&rmd.msg_length, RMDM, MCNT, size); | ||
1192 | SET_FIELD(&rmd.status, RMDS, ENP, 1); | 1209 | SET_FIELD(&rmd.status, RMDS, ENP, 1); |
1193 | SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr); | 1210 | SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr); |
1194 | SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr); | 1211 | SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr); |
1195 | SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast); | 1212 | SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast); |
1213 | + if (crc_err) { | ||
1214 | + SET_FIELD(&rmd.status, RMDS, CRC, 1); | ||
1215 | + SET_FIELD(&rmd.status, RMDS, ERR, 1); | ||
1216 | + } | ||
1196 | } else { | 1217 | } else { |
1197 | SET_FIELD(&rmd.status, RMDS, OFLO, 1); | 1218 | SET_FIELD(&rmd.status, RMDS, OFLO, 1); |
1198 | SET_FIELD(&rmd.status, RMDS, BUFF, 1); | 1219 | SET_FIELD(&rmd.status, RMDS, BUFF, 1); |
@@ -1229,6 +1250,8 @@ static void pcnet_transmit(PCNetState *s) | @@ -1229,6 +1250,8 @@ static void pcnet_transmit(PCNetState *s) | ||
1229 | { | 1250 | { |
1230 | target_phys_addr_t xmit_cxda = 0; | 1251 | target_phys_addr_t xmit_cxda = 0; |
1231 | int count = CSR_XMTRL(s)-1; | 1252 | int count = CSR_XMTRL(s)-1; |
1253 | + int add_crc = 0; | ||
1254 | + | ||
1232 | s->xmit_pos = -1; | 1255 | s->xmit_pos = -1; |
1233 | 1256 | ||
1234 | if (!CSR_TXON(s)) { | 1257 | if (!CSR_TXON(s)) { |
@@ -1251,6 +1274,8 @@ static void pcnet_transmit(PCNetState *s) | @@ -1251,6 +1274,8 @@ static void pcnet_transmit(PCNetState *s) | ||
1251 | if (GET_FIELD(tmd.status, TMDS, STP)) { | 1274 | if (GET_FIELD(tmd.status, TMDS, STP)) { |
1252 | s->xmit_pos = 0; | 1275 | s->xmit_pos = 0; |
1253 | xmit_cxda = PHYSADDR(s,CSR_CXDA(s)); | 1276 | xmit_cxda = PHYSADDR(s,CSR_CXDA(s)); |
1277 | + if (BCR_SWSTYLE(s) != 1) | ||
1278 | + add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS); | ||
1254 | } | 1279 | } |
1255 | if (!GET_FIELD(tmd.status, TMDS, ENP)) { | 1280 | if (!GET_FIELD(tmd.status, TMDS, ENP)) { |
1256 | int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); | 1281 | int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); |
@@ -1265,9 +1290,13 @@ static void pcnet_transmit(PCNetState *s) | @@ -1265,9 +1290,13 @@ static void pcnet_transmit(PCNetState *s) | ||
1265 | #ifdef PCNET_DEBUG | 1290 | #ifdef PCNET_DEBUG |
1266 | printf("pcnet_transmit size=%d\n", s->xmit_pos); | 1291 | printf("pcnet_transmit size=%d\n", s->xmit_pos); |
1267 | #endif | 1292 | #endif |
1268 | - if (CSR_LOOP(s)) | 1293 | + if (CSR_LOOP(s)) { |
1294 | + if (BCR_SWSTYLE(s) == 1) | ||
1295 | + add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); | ||
1296 | + s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; | ||
1269 | pcnet_receive(s, s->buffer, s->xmit_pos); | 1297 | pcnet_receive(s, s->buffer, s->xmit_pos); |
1270 | - else | 1298 | + s->looptest = 0; |
1299 | + } else | ||
1271 | if (s->vc) | 1300 | if (s->vc) |
1272 | qemu_send_packet(s->vc, s->buffer, s->xmit_pos); | 1301 | qemu_send_packet(s->vc, s->buffer, s->xmit_pos); |
1273 | 1302 |