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 |