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 | ... | ... |