📄 e100.c
字号:
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 ) _val = (uint8_t)val; else if ( bytes == WRITEW ) _val = (val & 0xff00) >> 8; else logout("WARNNING: Drvier write 4 bytes to CSR register at offset %d," "emulator may do things wrong!!!\n", addr_offset); // Driver generates a software interrupt if ( _val & BIT(1) ) e100_interrupt(s, INT_SWI); } break; case SCB_PORT ... SCB_PORT + 3: if ( dir == OP_WRITE ) { // Waitting for driver write to the highest byte if ( (bytes == WRITEB && addr_offset != SCB_PORT + 3) || (bytes == WRITEW && addr_offset != SCB_PORT + 2) ) break; scb_port_func(s, CSR_VAL(CSR_PORT), dir); } break; case SCB_MDI ... SCB_MDI + 3: if ( dir == OP_WRITE ) { // Waitting for driver write to the highest byte if ( (bytes == WRITEB && addr_offset != SCB_MDI + 3) || (bytes == WRITEW && addr_offset != SCB_MDI + 2) ) break; } scb_mdi_func(s, CSR_VAL(CSR_MDI), dir); break; case SCB_EEPROM: if ( dir == OP_WRITE ) scb_eeprom_func(s, val, dir); // Nothing need do when driver read EEPROM registers of CSR break; case SCB_POINTER: break; default: logout("Driver operate on CSR reg(offset=%#x,dir=%s,val=%#x)\n", addr_offset, dir==OP_WRITE?"write":"read", val); }}/* MMIO access functions */static uint8_t e100_read1(E100State * s, uint32_t addr_offset){ uint8_t val = -1; if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) ) { logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset + s->region_base_addr[CSR_MEMORY_BASE]); return val; } e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ); val = CSR_READ(addr_offset, uint8_t); logout("READ1: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val); return val;}static uint16_t e100_read2(E100State * s, uint32_t addr_offset){ uint16_t val = -1; if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) ) { logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset + s->region_base_addr[CSR_MEMORY_BASE]); return val; } e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ); val = CSR_READ(addr_offset, uint16_t); logout("READ2: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val); return val;}static uint32_t e100_read4(E100State * s, uint32_t addr_offset){ uint32_t val = -1; if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) ) { logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset + s->region_base_addr[CSR_MEMORY_BASE]); return val; } e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ); val = CSR_READ(addr_offset, uint32_t); logout("READ4: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val); return val;}static uint32_t pci_mmio_readb(void *opaque, target_phys_addr_t addr){ E100State *s = opaque; addr -= s->region_base_addr[CSR_MEMORY_BASE]; return e100_read1(s, addr);}static uint32_t pci_mmio_readw(void *opaque, target_phys_addr_t addr){ E100State *s = opaque; addr -= s->region_base_addr[CSR_MEMORY_BASE]; return e100_read2(s, addr);}static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr){ E100State *s = opaque; addr -= s->region_base_addr[CSR_MEMORY_BASE]; return e100_read4(s, addr);}static CPUReadMemoryFunc *pci_mmio_read[] = { pci_mmio_readb, pci_mmio_readw, pci_mmio_readl};static void e100_write1(E100State * s, uint32_t addr_offset, uint8_t val){ if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) ) { logout("Invaild write, beyond memory boundary(addr = %#x, val = %#x\n", addr_offset + s->region_base_addr[CSR_MEMORY_BASE], val); return; } // SCB stauts is read-only word, can not be directly write if ( addr_offset == SCB_STATUS ) { return; } // EEDO bit of eeprom register is read-only, can not be written; else if ( addr_offset == SCB_EEPROM ) { int eedo = BIT(3) & CSR_VAL(CSR_EEPROM); CSR_WRITE(addr_offset, val, uint8_t); CSR(CSR_EEPROM, eedo) = !!(eedo & EEPROM_DO); logout("WRITE1: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, (uint8_t)CSR_VAL(CSR_EEPROM));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -