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 | 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 | 40 | #include "hw.h" |
39 | 41 | #include "pci.h" |
40 | 42 | #include "net.h" |
... | ... | @@ -52,6 +54,9 @@ |
52 | 54 | #define PCNET_IOPORT_SIZE 0x20 |
53 | 55 | #define PCNET_PNPMMIO_SIZE 0x20 |
54 | 56 | |
57 | +#define PCNET_LOOPTEST_CRC 1 | |
58 | +#define PCNET_LOOPTEST_NOCRC 2 | |
59 | + | |
55 | 60 | |
56 | 61 | typedef struct PCNetState_st PCNetState; |
57 | 62 | |
... | ... | @@ -76,6 +81,7 @@ struct PCNetState_st { |
76 | 81 | void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr, |
77 | 82 | uint8_t *buf, int len, int do_bswap); |
78 | 83 | void *dma_opaque; |
84 | + int looptest; | |
79 | 85 | }; |
80 | 86 | |
81 | 87 | struct qemu_ether_header { |
... | ... | @@ -120,6 +126,7 @@ struct qemu_ether_header { |
120 | 126 | #define CSR_DRX(S) !!(((S)->csr[15])&0x0001) |
121 | 127 | #define CSR_DTX(S) !!(((S)->csr[15])&0x0002) |
122 | 128 | #define CSR_LOOP(S) !!(((S)->csr[15])&0x0004) |
129 | +#define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008) | |
123 | 130 | #define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000) |
124 | 131 | #define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000) |
125 | 132 | #define CSR_PROM(S) !!(((S)->csr[15])&0x8000) |
... | ... | @@ -202,6 +209,8 @@ struct pcnet_TMD { |
202 | 209 | #define TMDS_LTINT_SH 12 |
203 | 210 | #define TMDS_NOFCS_MASK 0x2000 |
204 | 211 | #define TMDS_NOFCS_SH 13 |
212 | +#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK | |
213 | +#define TMDS_ADDFCS_SH TMDS_NOFCS_SH | |
205 | 214 | #define TMDS_ERR_MASK 0x4000 |
206 | 215 | #define TMDS_ERR_SH 14 |
207 | 216 | #define TMDS_OWN_MASK 0x8000 |
... | ... | @@ -1064,6 +1073,8 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) |
1064 | 1073 | PCNetState *s = opaque; |
1065 | 1074 | int is_padr = 0, is_bcast = 0, is_ladr = 0; |
1066 | 1075 | uint8_t buf1[60]; |
1076 | + int remaining; | |
1077 | + int crc_err = 0; | |
1067 | 1078 | |
1068 | 1079 | if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size) |
1069 | 1080 | return; |
... | ... | @@ -1117,37 +1128,36 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) |
1117 | 1128 | s->csr[0] |= 0x1000; /* Set MISS flag */ |
1118 | 1129 | CSR_MISSC(s)++; |
1119 | 1130 | } else { |
1120 | - uint8_t *src = &s->buffer[8]; | |
1131 | + uint8_t *src = s->buffer; | |
1121 | 1132 | target_phys_addr_t crda = CSR_CRDA(s); |
1122 | 1133 | struct pcnet_RMD rmd; |
1123 | 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 | 1146 | uint32_t fcs = ~0; |
1138 | 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 | 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 | 1162 | #ifdef PCNET_DEBUG_MATCH |
1153 | 1163 | PRINT_PKTHDR(buf); |
... | ... | @@ -1158,24 +1168,30 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) |
1158 | 1168 | SET_FIELD(&rmd.status, RMDS, STP, 1); |
1159 | 1169 | |
1160 | 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 | 1172 | target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \ |
1163 | 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 | 1175 | SET_FIELD(&rmd.status, RMDS, OWN, 0); \ |
1167 | 1176 | RMDSTORE(&rmd, PHYSADDR(s,crda)); \ |
1168 | 1177 | pktcount++; \ |
1169 | 1178 | } while (0) |
1170 | 1179 | |
1180 | + remaining = size; | |
1171 | 1181 | PCNET_RECV_STORE(); |
1172 | - if ((size > 0) && CSR_NRDA(s)) { | |
1182 | + if ((remaining > 0) && CSR_NRDA(s)) { | |
1173 | 1183 | target_phys_addr_t nrda = CSR_NRDA(s); |
1184 | +#ifdef PCNET_DEBUG_RMD | |
1185 | + PRINT_RMD(&rmd); | |
1186 | +#endif | |
1174 | 1187 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); |
1175 | 1188 | if (GET_FIELD(rmd.status, RMDS, OWN)) { |
1176 | 1189 | crda = nrda; |
1177 | 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 | 1195 | RMDLOAD(&rmd, PHYSADDR(s,nrda)); |
1180 | 1196 | if (GET_FIELD(rmd.status, RMDS, OWN)) { |
1181 | 1197 | crda = nrda; |
... | ... | @@ -1188,11 +1204,16 @@ static void pcnet_receive(void *opaque, const uint8_t *buf, int size) |
1188 | 1204 | #undef PCNET_RECV_STORE |
1189 | 1205 | |
1190 | 1206 | RMDLOAD(&rmd, PHYSADDR(s,crda)); |
1191 | - if (size == 0) { | |
1207 | + if (remaining == 0) { | |
1208 | + SET_FIELD(&rmd.msg_length, RMDM, MCNT, size); | |
1192 | 1209 | SET_FIELD(&rmd.status, RMDS, ENP, 1); |
1193 | 1210 | SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr); |
1194 | 1211 | SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr); |
1195 | 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 | 1217 | } else { |
1197 | 1218 | SET_FIELD(&rmd.status, RMDS, OFLO, 1); |
1198 | 1219 | SET_FIELD(&rmd.status, RMDS, BUFF, 1); |
... | ... | @@ -1229,6 +1250,8 @@ static void pcnet_transmit(PCNetState *s) |
1229 | 1250 | { |
1230 | 1251 | target_phys_addr_t xmit_cxda = 0; |
1231 | 1252 | int count = CSR_XMTRL(s)-1; |
1253 | + int add_crc = 0; | |
1254 | + | |
1232 | 1255 | s->xmit_pos = -1; |
1233 | 1256 | |
1234 | 1257 | if (!CSR_TXON(s)) { |
... | ... | @@ -1251,6 +1274,8 @@ static void pcnet_transmit(PCNetState *s) |
1251 | 1274 | if (GET_FIELD(tmd.status, TMDS, STP)) { |
1252 | 1275 | s->xmit_pos = 0; |
1253 | 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 | 1280 | if (!GET_FIELD(tmd.status, TMDS, ENP)) { |
1256 | 1281 | int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); |
... | ... | @@ -1265,9 +1290,13 @@ static void pcnet_transmit(PCNetState *s) |
1265 | 1290 | #ifdef PCNET_DEBUG |
1266 | 1291 | printf("pcnet_transmit size=%d\n", s->xmit_pos); |
1267 | 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 | 1297 | pcnet_receive(s, s->buffer, s->xmit_pos); |
1270 | - else | |
1298 | + s->looptest = 0; | |
1299 | + } else | |
1271 | 1300 | if (s->vc) |
1272 | 1301 | qemu_send_packet(s->vc, s->buffer, s->xmit_pos); |
1273 | 1302 | ... | ... |