From f1e3216d99319ed18e727f782faac54e2d1f0b56 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 5 Nov 2012 15:12:36 -0800 Subject: [PATCH] Added MicroVAX I network boot support pdp11_xq.c, pdp11_xq.h - Added emulation and visibility to the LEDs which were on the physical DEQNA/DELQA network boards. "show xq: will now display the LED state in addition to the other useful things. - Added debugging of loopback packet data - Avoided padding on short loopback packets - Added support for extended length loopback packets (up to 1600 bytes) which is described in the DEQNA manual and used by the MicroVAX I boot ROM. Recieve such packets with the LONG error indicator. - Returned 'reserved' status bits as 1's in received packet status word 1. - Added debug display of transmit and receive Buffer Descriptor List contents. sim_ether.c, sim_ether.h - Added support for extended/oversized packets. --- PDP11/pdp11_xq.c | 140 +++++++++++++++++++++++++++++++++++++++++------ PDP11/pdp11_xq.h | 2 + sim_ether.c | 26 +++++++-- sim_ether.h | 1 + 4 files changed, 149 insertions(+), 20 deletions(-) diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index 4937fe4d..85308522 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -256,6 +256,7 @@ extern int32 tmxr_poll; extern int32 tmr_poll, clk_tps; extern t_bool sim_idle_enab; extern FILE* sim_deb; +extern int32 sim_switches; extern FILE *sim_log; extern char* read_line (char *ptr, int32 size, FILE *stream); @@ -278,6 +279,7 @@ t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_leds (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_process_xbdl(CTLR* xq); t_stat xq_dispatch_xbdl(CTLR* xq); t_stat xq_process_turbo_rbdl(CTLR* xq); @@ -458,6 +460,8 @@ MTAB xq_mod[] = { #endif { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", &xq_set_sanity, &xq_show_sanity, NULL }, + { MTAB_XTD | MTAB_VDV , 0, "LEDS", "LEDS", + NULL, &xq_show_leds, NULL }, { 0 }, }; @@ -822,6 +826,16 @@ t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc) return SCPE_OK; } +t_stat xq_show_leds (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + fprintf(st, "leds=(%s,%s,%s)", xq->var->setup.l1 ? "ON" : "OFF", + xq->var->setup.l2 ? "ON" : "OFF", + xq->var->setup.l3 ? "ON" : "OFF"); + return SCPE_OK; +} + /*============================================================================*/ t_stat xq_nxm_error(CTLR* xq) @@ -985,6 +999,8 @@ t_stat xq_process_rbdl(CTLR* xq) item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; rbl = item->packet.len; rbuf = item->packet.msg; + if (item->packet.oversize) + rbuf = item->packet.oversize; /* see if packet must be size-adjusted or is splitting */ if (item->packet.used) { @@ -992,8 +1008,11 @@ t_stat xq_process_rbdl(CTLR* xq) rbl -= used; rbuf = &item->packet.msg[used]; } else { - /* adjust runt packets */ - if (rbl < ETH_MIN_PACKET) { + /* there should be no need to adjust runt packets + the physical layer (sim_ether) won't deliver any short packets + via eth_read, so the only short packets which get here are loopback + packets sent by the host diagnostics */ + if ((item->type != 1) && (rbl < ETH_MIN_PACKET)) { xq->var->stats.runt += 1; sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) @@ -1003,12 +1022,14 @@ t_stat xq_process_rbdl(CTLR* xq) }; /* adjust oversized packets */ - if (rbl > ETH_MAX_PACKET) { + if (rbl > ETH_FRAME_SIZE) { xq->var->stats.giant += 1; sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); /* trim giants down to maximum size - no documentation on how to handle the data loss */ - item->packet.len = ETH_MAX_PACKET; - rbl = ETH_MAX_PACKET; + if (rbl > XQ_MAX_RCV_PACKET) { + item->packet.len = XQ_MAX_RCV_PACKET; + rbl = XQ_MAX_RCV_PACKET; + } }; }; @@ -1038,6 +1059,7 @@ t_stat xq_process_rbdl(CTLR* xq) case 2: /* normal packet */ rbl -= 60; /* keeps max packet size in 11 bits */ xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */ + xq->var->rbdl_buf[4] |= 0x00f8; /* set reserved bits to 1 */ break; } if (item->packet.used < item->packet.len) @@ -1048,12 +1070,16 @@ t_stat xq_process_rbdl(CTLR* xq) xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ xq->var->stats.dropped += xq->var->ReadQ.loss; xq->var->ReadQ.loss = 0; /* reset loss counter */ - } + } + if ((rbl + ((item->type == 2) ? 60 : 0)) > ETH_MAX_PACKET) + xq->var->rbdl_buf[4] |= 0x4000; /* set Error bit (LONG) */ /* update read status words*/ wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (wstatus) return xq_nxm_error(xq); + sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl(bd=0x%X, addr=0x%X, size=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X)\n", xq->var->rbdl_ba, address, b_length, rbl + ((item->type == 2) ? 60 : 0), xq->var->rbdl_buf[4], xq->var->rbdl_buf[5]); + /* remove packet from queue */ if (item->packet.used >= item->packet.len) ethq_remove(&xq->var->ReadQ); @@ -1263,6 +1289,8 @@ t_stat xq_process_xbdl(CTLR* xq) /* clear write buffer */ xq->var->write_buffer.len = 0; + free (xq->var->write_buffer.oversize); + xq->var->write_buffer.oversize = NULL; /* process buffer descriptors until not valid */ while (1) { @@ -1297,9 +1325,12 @@ t_stat xq_process_xbdl(CTLR* xq) } /* add to transmit buffer, making sure it's not too big */ - if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) - b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); - rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); + if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) { + xq->var->write_buffer.oversize = realloc (xq->var->write_buffer.oversize, xq->var->write_buffer.len + b_length); + if (xq->var->write_buffer.len <= sizeof(xq->var->write_buffer.msg)) + memcpy (xq->var->write_buffer.oversize, xq->var->write_buffer.msg, xq->var->write_buffer.len); + } + rstatus = Map_ReadB(address, b_length, xq->var->write_buffer.oversize ? &xq->var->write_buffer.oversize[xq->var->write_buffer.len] : &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (rstatus) return xq_nxm_error(xq); xq->var->write_buffer.len += b_length; @@ -1315,6 +1346,8 @@ t_stat xq_process_xbdl(CTLR* xq) } else { /* loopback */ /* put packet in read buffer */ ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); + if ((DBG_PCK & xq->dev->dctrl) && xq->var->etherface) + eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write-loopback", DBG_DAT & xq->dev->dctrl, DBG_PCK); } /* update write status */ @@ -1323,6 +1356,8 @@ t_stat xq_process_xbdl(CTLR* xq) /* clear write buffer */ xq->var->write_buffer.len = 0; + free (xq->var->write_buffer.oversize); + xq->var->write_buffer.oversize = NULL; /* reset sanity timer */ xq_reset_santmr(xq); @@ -1361,6 +1396,54 @@ t_stat xq_process_xbdl(CTLR* xq) } /* while */ } +void xq_show_debug_bdl(CTLR* xq, uint32 bdl_ba) +{ + uint16 bdl_buf[6]; + uint16 b_length, w_length; + uint32 address, initial_bdl_ba = bdl_ba; + int32 rstatus; + + sim_debug(DBG_TRC, xq->dev, " Descriptor list at: 0x%X\n", bdl_ba); + + while (1) { + + /* get the beginning of the buffer descriptor */ + rstatus = Map_ReadW (bdl_ba, 4, &bdl_buf[0]); + if (rstatus) return; + + /* invalid buffer? */ + if (~bdl_buf[1] & XQ_DSC_V) + break; + + /* get the rest of the buffer descriptor */ + rstatus = Map_ReadW (bdl_ba + 4, 8, &bdl_buf[2]); + if (rstatus) return; + + /* explicit chain buffer? */ + if (bdl_buf[1] & XQ_DSC_C) { + sim_debug(DBG_TRC, xq->dev, " descriptor=0x%X, flags=0x%04X, chain=0x%X\n", bdl_ba, bdl_buf[0], ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2]); + bdl_ba = ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2]; + if (initial_bdl_ba == bdl_ba) + break; + continue; + } + + /* get host memory address */ + address = ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~bdl_buf[3] + 1; + b_length = w_length * 2; + if (bdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (bdl_buf[1] & XQ_DSC_L) b_length -= 1; + + sim_debug(DBG_TRC, xq->dev, " descriptor=0x%X, flags=0x%04X, bits=0x%04X, addr=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X\n", + bdl_ba, bdl_buf[0], bdl_buf[1] & 0xFFC0, address, b_length, bdl_buf[4], bdl_buf[5]); + + bdl_ba += 12; + } +} + t_stat xq_dispatch_rbdl(CTLR* xq) { int i; @@ -1388,7 +1471,10 @@ t_stat xq_dispatch_rbdl(CTLR* xq) if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; - } + } + + /* When debugging, walk and display the buffer descriptor list */ + xq_show_debug_bdl(xq, xq->var->rbdl_ba); /* process any waiting packets in receive queue */ if (xq->var->ReadQ.count) @@ -1413,10 +1499,15 @@ t_stat xq_dispatch_xbdl(CTLR* xq) /* clear transmit buffer */ xq->var->write_buffer.len = 0; + free (xq->var->write_buffer.oversize); + xq->var->write_buffer.oversize = NULL; /* get base address of first transmit descriptor */ xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01); + /* When debugging, walk and display the buffer descriptor list */ + xq_show_debug_bdl(xq, xq->var->xbdl_ba); + /* process xbdl */ status = xq_process_xbdl(xq); @@ -1475,8 +1566,8 @@ t_stat xq_process_turbo_rbdl(CTLR* xq) rbl -= used; rbuf = &item->packet.msg[used]; } else { - /* adjust runt packets */ - if (rbl < ETH_MIN_PACKET) { + /* adjust non loopback runt packets */ + if ((item->type != 1) && (rbl < ETH_MIN_PACKET)) { xq->var->stats.runt += 1; sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) @@ -1564,6 +1655,8 @@ t_stat xq_process_turbo_xbdl(CTLR* xq) /* clear transmit buffer */ xq->var->write_buffer.len = 0; + free (xq->var->write_buffer.oversize); + xq->var->write_buffer.oversize = NULL; /* Process each descriptor in the transmit ring */ do { @@ -1587,10 +1680,13 @@ t_stat xq_process_turbo_xbdl(CTLR* xq) address = ((xq->var->xring[i].hadr & 0x3F ) << 16) | xq->var->xring[i].ladr; b_length = (xq->var->xring[i].tmd3 & XQ_TMD3_BCT); - /* add to transmit buffer, making sure it's not too big */ - if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) - b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); - status = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); + /* add to transmit buffer, accomodating it if it is too big */ + if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) { + xq->var->write_buffer.oversize = realloc (xq->var->write_buffer.oversize, xq->var->write_buffer.len + b_length); + if (xq->var->write_buffer.len <= sizeof(xq->var->write_buffer.msg)) + memcpy (xq->var->write_buffer.oversize, xq->var->write_buffer.msg, xq->var->write_buffer.len); + } + status = Map_ReadB(address, b_length, xq->var->write_buffer.oversize ? &xq->var->write_buffer.oversize[xq->var->write_buffer.len] : &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (status != SCPE_OK) return xq_nxm_error(xq); @@ -2086,6 +2182,11 @@ t_stat xq_process_bootrom (CTLR* xq) /* reset sanity timer */ xq_reset_santmr(xq); + /* Turn on all 3 DEQNA Leds */ + xq->var->setup.l1 = 1; + xq->var->setup.l2 = 1; + xq->var->setup.l3 = 1; + return SCPE_OK; } #endif /* ifdef VM_PDP11 */ @@ -2387,6 +2488,13 @@ t_stat xq_reset(DEVICE* dptr) xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; } + if (sim_switches & SWMASK ('P')) { /* Powerup? */ + /* Turn on all 3 DEQNA Leds */ + xq->var->setup.l1 = 1; + xq->var->setup.l2 = 1; + xq->var->setup.l3 = 1; + } + return auto_config (0, 0); /* run autoconfig */ } diff --git a/PDP11/pdp11_xq.h b/PDP11/pdp11_xq.h index b71a725e..4f76ee75 100644 --- a/PDP11/pdp11_xq.h +++ b/PDP11/pdp11_xq.h @@ -96,6 +96,8 @@ extern int32 int_req[IPL_HLVL]; #define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ #define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ +#define XQ_MAX_RCV_PACKET 1600 /* Maximum receive packet data */ + enum xq_type {XQ_T_DEQNA, XQ_T_DELQA, XQ_T_DELQA_PLUS}; struct xq_sanity { diff --git a/sim_ether.c b/sim_ether.c index e49d0a5c..44677a3c 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -757,6 +757,14 @@ t_stat ethq_destroy(ETH_QUE* que) void ethq_clear(ETH_QUE* que) { + int i; + + /* free up any extended packets */ + for (i=0; imax; ++i) + if (que->item[i].packet.oversize) { + free (que->item[i].packet.oversize); + que->item[i].packet.oversize = NULL; + } /* clear packet array */ memset(que->item, 0, sizeof(struct eth_item) * que->max); /* clear rest of structure */ @@ -768,6 +776,8 @@ void ethq_remove(ETH_QUE* que) struct eth_item* item = &que->item[que->head]; if (que->count) { + if (item->packet.oversize) + free (item->packet.oversize); memset(item, 0, sizeof(struct eth_item)); if (++que->head == que->max) que->head = 0; @@ -804,15 +814,23 @@ void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, int item->packet.len = len; item->packet.used = used; item->packet.crc_len = crc_len; - memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); - if (crc_data && (crc_len > len)) - memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); + if (len <= sizeof (item->packet.msg)) { + memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); + if (crc_data && (crc_len > len)) + memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); + } + else { + item->packet.oversize = realloc (item->packet.oversize, ((len > crc_len) ? len : crc_len)); + memcpy(item->packet.oversize, data, ((len > crc_len) ? len : crc_len)); + if (crc_data && (crc_len > len)) + memcpy(&item->packet.oversize[len], crc_data, ETH_CRC_SIZE); + } item->packet.status = status; } void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) { - ethq_insert_data(que, type, pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); +ethq_insert_data(que, type, pack->oversize ? pack->oversize : pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); } /*============================================================================*/ diff --git a/sim_ether.h b/sim_ether.h index 7706ffef..36cb11f5 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -173,6 +173,7 @@ struct eth_packet { uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ + uint8 *oversize; /* oversized frame (message) */ uint32 len; /* packet length without CRC */ uint32 used; /* bytes processed (used in packet chaining) */ int status; /* transmit/receive status */