📄 e100.c
字号:
cpu_physical_memory_read(tbd_array, (uint8_t *)&tx_buf, sizeof(tx_buf)); tx_buf.is_el_set &= 0x1; tx_buf.size &= 0x7fff; tbd_array += 8; if ( tx_buf.size > sizeof(s->pkt_buf) - len ) { logout("Warning: Get a too big TBD, ignore it" "(buf addr %#x, size %d, el:%#x)\n", tx_buf.addr, tx_buf.size, tx_buf.is_el_set); continue; } cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len], tx_buf.size); logout("TBD (standard mode): buf addr %#x, size %d, el:%#x\n", tx_buf.addr, tx_buf.size, tx_buf.is_el_set); len += tx_buf.size; if ( tx_buf.is_el_set ) break; } } //FIXME: Extend mode is not be tested else { /* Extend TCB mode */ /* A strandard TCB followed by two TBDs */ uint32_t tbd_addr = cb_addr+16; int i = 0; for ( ; i<2 && i<tx.tbd.tbd_num; i++ ) { cpu_physical_memory_read(tbd_array, (uint8_t *)&tx_buf, sizeof(tx_buf)); tx_buf.is_el_set &= 0x1; tbd_addr += 8; /* From Intel's spec, size of TBD equal to zero * has same effect with EL bit set */ if ( tx_buf.size == 0 ) { tx_buf.is_el_set = 1; break; } if ( tx_buf.size + len > sizeof(s->pkt_buf) ) { logout("TX frame is too large, discarding it" "(buf addr=%#x, size=%#x)\n", tx_buf.addr, tx_buf.size); //continue; break; } logout("TBD (extended mode): buf addr %#08x, size %#04x, el:%#x\n", tx_buf.addr, tx_buf.size, tx_buf.is_el_set); cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len], tx_buf.size); len += tx_buf.size; if ( tx_buf.is_el_set ) break; } /* In extend TCB mode, TDB array point to the thrid TBD * if it is not NULL(0xffffffff) and EL bit of before * two TBDs is not set */ if ( tbd_array != (uint32_t)-1 && !tx_buf.is_el_set ) { tbd_addr = tbd_array; /* TBD number includes first two TBDs, so don't * initialize i here */ for ( ; i<tx.tbd.tbd_num; i++ ) { cpu_physical_memory_read(tbd_addr, (uint8_t *)&tx_buf, sizeof(tx_buf)); tx_buf.is_el_set &= 0x1; tbd_addr += 8; cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len], tx_buf.size); logout("TBD (extended mode): buf addr 0x%#08x, size 0x%#04x\n", tx_buf.addr, tx_buf.size); len += tx_buf.size; if ( tx_buf.is_el_set ) break; } } } } s->pkt_buf_len = len;/* Below codes are used for Threshold. But with these logic, network of guest * getting bad performance. So I comment it and leave codes here to hope anyone * fix it */#if 0 /* If threshold is set, only send packet when threshold * bytes are read */ if ( tx.tbd.tx_threshold && s->pkt_buf_len < tx.tbd.tx_threshold * 8 ) { logout("Current data length in FIFO buffer:%d\n", s->pkt_buf_len); break; }#endif if ( s->pkt_buf_len ) { qemu_send_packet(s->vc, s->pkt_buf, s->pkt_buf_len); s->statistics.tx_good_frames ++; logout("Send out frame successful(size=%d," "already sent %d frames)\n", s->pkt_buf_len, s->statistics.tx_good_frames); s->pkt_buf_len = 0; } e100_dump("Dest addr:", (uint8_t *)s->pkt_buf, 6); e100_dump("Src addr:", (uint8_t *)(s->pkt_buf+6), 6); e100_dump("type:", (uint8_t *)(s->pkt_buf+8), 2); break; } case CBL_LOAD_MICROCODE:#ifdef DEBUG_E100 { /* Don't support load marco code, just dump it */ #define MICRO_CODE_LEN 256 uint8_t micro_code[MICRO_CODE_LEN] = {0}; cpu_physical_memory_read(cb_addr+8, micro_code, MICRO_CODE_LEN); e100_dump("Load micro code:", micro_code, MICRO_CODE_LEN); }#endif break; case CBL_DUMP: logout("Control block dump\n"); break; case CBL_DIAGNOSE: logout("Control block diagnose\n"); break; default: logout("Unknown Control block command(val=%#x)\n", cb.cmd); break; } /* Now, we finished executing a command, update status of CB. * We always success */ cb.c = 1; cb.ok = 1; // Only update C bit and OK bit field in TCB cpu_physical_memory_write(cb_addr, (uint8_t *)&cb, 2); logout("Finished a command from CB list:\n" "\tok:%d\n" "\tc:%d\n" "\tcommand name:%s(cmd=%#x)\n" "\ti:%d\n" "\ts:%d\n" "\tel:%d\n" "\tlink address:%#x\n", cb.ok, cb.c, CB_CMD_NAME(cb.cmd), cb.cmd, cb.i, cb.s, cb.el, cb.link_addr); if ( cb.i ) e100_interrupt(s, (uint16_t)INT_CX_TNO); // Suspend CU if ( cb.s ) { logout("CU go to suspend\n"); SET_CU_STATE(CU_SUSPENDED); s->cu_next = cb.link_addr; // Save it for go on executing when resume // Trigger CNA interrupt only when CNA mode is configured if ( !(s->config.ci_intr) && cb.i ) e100_interrupt(s, (uint16_t)INT_CNA); return; } // This is last command in CB list, CU go back to IDLE if ( cb.el ) { logout("Command block list is empty, CU go to idle\n"); SET_CU_STATE(CU_IDLE); /* Either in CNA mode or CI mode, interrupt need be triggered * when CU go to idle. */ if ( cb.i ) e100_interrupt(s, (uint16_t)INT_CNA); return; } s->cu_offset = le32_to_cpu(cb.link_addr); // get next CB offset }}static void dump_statistics(E100State * s, uint32_t complete_word){ /* Dump statistical data. Most data is never changed by the emulation * and always 0. */ s->statistics.complete_word = complete_word; cpu_physical_memory_write(s->statsaddr, (uint8_t *)&s->statistics, sizeof(s->statistics));}static void e100_cu_command(E100State *s, uint8_t val){ switch ( val ) { case CU_NOP: /* Will not be here */ break; case CU_START: /* This strictly follow Intel's spec */ if ( GET_CU_STATE != CU_IDLE && GET_CU_STATE != CU_SUSPENDED ) { logout("Illegal CU start command. Device is not idle or suspend\n"); return; } SET_CU_STATE(CU_LPQ_ACTIVE); logout("CU start\n"); e100_execute_cb_list(s, 0); break; case CU_RESUME: { uint32_t previous_cb = s->cu_base + s->cu_offset; struct control_block cb; /* Resume from suspend */ /* FIXME:From Intel's spec, CU resume from idle is * forbidden, but e100 drive in linux * indeed do this. */ if ( GET_CU_STATE == CU_IDLE ) { logout("Illegal resume form IDLE\n"); } cpu_physical_memory_read(previous_cb, (uint8_t *)&cb, sizeof(cb)); //FIXME: Need any speical handle when CU is active ? /* Driver must clean S bit in previous CB when * it issue CU resume command */ if ( cb.s ) { logout("CU still in suspend\n"); break; } SET_CU_STATE(CU_LPQ_ACTIVE); if ( cb.el ) { logout("CB list is empty, CU just go to active\n"); break; } // Continue next command s->cu_offset = s->cu_next; e100_execute_cb_list(s, 1); logout("CU resume\n"); } break; case CU_STATSADDR: /* Load dump counters address */ s->statsaddr = CSR_VAL(CSR_POINTER); logout("Load Stats address at %#x\n", s->statsaddr); break; case CU_SHOWSTATS: /* Dump statistical counters */ dump_statistics(s, 0xa005); logout("Execute dump statistics\n"); break; case CU_CMD_BASE: /* Load CU base */ s->cu_base = CSR_VAL(CSR_POINTER); logout("Load CU base at %x\n", s->cu_base); break; case CU_DUMPSTATS: /* Dump statistical counters and reset counters. */ dump_statistics(s, 0xa007); memset(&s->statistics, 0x0, sizeof(s->statistics)); logout("Execute dump and reset statistics\n"); break; case CU_S_RESUME: /* CU static resume */ logout("CU static resume is not implemented\n"); break; default: logout("Unknown CU command(val=%#x)\n", val); break; }}static void scb_cmd_func(E100State *s, uint16_t val, int dir){ /* ignore NOP operation */ if ( val & 0x0f ) { e100_ru_command(s, val & 0x0f); CSR(CSR_CMD, ru_cmd) = 0; } else if ( val & 0xf0 ) { e100_cu_command(s, val & 0xf0); CSR(CSR_CMD, cu_cmd) = 0; }}enum{ WRITEB, WRITEW, WRITEL, OP_IS_READ,} WRITE_BYTES;/* Driver may issue a command by writting one 32bit-entry, * two 16bit-entries or four 8bit-entries. In late two case, we * must wait until driver finish writting to the highest byte. The parameter * 'bytes' means write action of driver(writeb, wirtew, wirtel) */static void e100_execute(E100State *s, uint32_t addr_offset, uint32_t val, int dir, int bytes){ switch ( addr_offset ) { case SCB_STATUS: if ( bytes == WRITEB ) break; case SCB_ACK: if ( dir == OP_WRITE ) { uint8_t _val = 0; if ( bytes == WRITEB ) _val = (uint8_t)val; else if ( bytes == WRITEW ) _val = ((uint16_t)val) >> 8; else if ( bytes == WRITEL) { // This should not be happen _val = ((uint16_t)val) >> 8; logout("WARNNING: Drvier write 4 bytes to CSR register at offset %d," "emulator may do things wrong!!!\n", addr_offset); } e100_interrupt_ack(s, _val); } break; case SCB_CMD: if ( dir == OP_WRITE ) scb_cmd_func(s, val, dir);/* I don't know whether there is any driver writes command words and * interrupt mask at same time by two bytes. This is not a regular operation. * but if we meet the case, below codes could copy with it. As far * as I know. windows's and linux's driver don't do this thing. */#if 0 if ( bytes == WRITEW && (val&0xff00) != 0 ) ; else break;#endif break; case SCB_INTERRUPT_MASK: if ( dir == OP_WRITE ) { uint8_t _val = 0; if ( bytes == WRITEB )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -