/* Put a buffer to vtty */ void vtty_put_buffer(vtty_t *vtty,char *buf,size_t len) { size_t i; for(i=0;i<len;i++) vtty_put_char(vtty,buf[i]); vtty_flush(vtty); }
/* Read a character (until one is available) and store it in buffer */ static void vtty_read_and_store(vtty_t *vtty,int *fd_slot) { int c; /* wait until we get a character input */ c = vtty_read(vtty,fd_slot); /* if read error, do nothing */ if (c < 0) return; /* If something was read, make sure the handler is informed */ vtty->input_pending = TRUE; if (!vtty->terminal_support) { vtty_store(vtty,c); return; } switch(vtty->input_state) { case VTTY_INPUT_TEXT : switch(c) { case 0x1b: vtty->input_state = VTTY_INPUT_VT1; return; /* Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ case 0x1d: case 0xb3: if (ctrl_code_ok == 1) { vtty->input_state = VTTY_INPUT_REMOTE; } else { vtty_store(vtty,c); } return; case IAC : vtty->input_state = VTTY_INPUT_TELNET; return; case 0: /* NULL - Must be ignored - generated by Linux telnet */ case 10: /* LF (Line Feed) - Must be ignored on Windows platform */ return; default: /* Store a standard character */ vtty_store(vtty,c); return; } case VTTY_INPUT_VT1 : switch(c) { case 0x5b: vtty->input_state = VTTY_INPUT_VT2; return; default: vtty_store(vtty,0x1b); vtty_store(vtty,c); } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_VT2 : switch(c) { case 0x41: /* Up Arrow */ vtty_store(vtty,16); break; case 0x42: /* Down Arrow */ vtty_store(vtty,14); break; case 0x43: /* Right Arrow */ vtty_store(vtty,6); break; case 0x44: /* Left Arrow */ vtty_store(vtty,2); break; default: vtty_store(vtty,0x5b); vtty_store(vtty,0x1b); vtty_store(vtty,c); break; } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_REMOTE : remote_control(vtty, c); vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET : vtty->telnet_cmd = c; switch(c) { case WILL: case WONT: case DO: case DONT: vtty->input_state = VTTY_INPUT_TELNET_IYOU; return; case SB : vtty->telnet_cmd = c; vtty->input_state = VTTY_INPUT_TELNET_SB1; return; case SE: break; case IAC : vtty_store(vtty, IAC); break; } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET_IYOU : vtty->telnet_opt = c; /* if telnet client can support ttype, ask it to send ttype string */ if ((vtty->telnet_cmd == WILL) && (vtty->telnet_opt == TELOPT_TTYPE)) { vtty_put_char(vtty, IAC); vtty_put_char(vtty, SB); vtty_put_char(vtty, TELOPT_TTYPE); vtty_put_char(vtty, TELQUAL_SEND); vtty_put_char(vtty, IAC); vtty_put_char(vtty, SE); } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET_SB1 : vtty->telnet_opt = c; vtty->input_state = VTTY_INPUT_TELNET_SB2; return; case VTTY_INPUT_TELNET_SB2 : vtty->telnet_qual = c; if ((vtty->telnet_opt == TELOPT_TTYPE) && (vtty->telnet_qual == TELQUAL_IS)) vtty->input_state = VTTY_INPUT_TELNET_SB_TTYPE; else vtty->input_state = VTTY_INPUT_TELNET_NEXT; return; case VTTY_INPUT_TELNET_SB_TTYPE : /* parse ttype string: first char is sufficient */ /* if client is xterm or vt, set the title bar */ if ((c == 'x') || (c == 'X') || (c == 'v') || (c == 'V')) { fd_printf(*fd_slot,0,"\033]0;%s\07", vtty->vm->name); } vtty->input_state = VTTY_INPUT_TELNET_NEXT; return; case VTTY_INPUT_TELNET_NEXT : /* ignore all chars until next IAC */ if (c == IAC) vtty->input_state = VTTY_INPUT_TELNET; return; } }
/* * dev_ns16552_access() */ void *dev_ns16552_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct ns16552_data *d = dev->priv_data; int channel = 0; u_char odata; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"NS16552","write to 0x%x, value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif offset >>= d->reg_div; if (offset >= 0x08) channel = 1; // From the NS16552V datasheet, the following is known about the registers // Bit 4 is channel // Value 0 Receive or transmit buffer // Value 1 Interrupt enable // Value 2 Interrupt identification (READ), FIFO Config (Write) // Value 3 Line Control (Appears in IOS) // 0x1 - Word Length Selector bit 0 // 0x2 - Word Length Selector bit 1 // 0x4 - Num stop bits // 0x8 - Parity Enable // 0x16 - Parity even // 0x32 - Stick Parity // 0x64 - Set Break // 0x128 - Division Latch // Value 4 Modem Control (Appears in IOS) // Value 5 Line status // Value 6 Modem Status // Value 7 Scratch switch(offset) { /* Receiver Buffer Reg. (RBR) / Transmitting Holding Reg. (THR) */ case 0x00: case 0x08: if (d->div_latch == 0) { if (op_type == MTS_WRITE) { vtty_put_char(d->channel[channel].vtty,(char)*data); if (d->channel[channel].ier & IER_ETXRDY) vm_set_irq(d->vm,d->irq); d->channel[channel].output = TRUE; } else *data = vtty_get_char(d->channel[channel].vtty); } else { if (op_type == MTS_WRITE) d->baud_divisor = ((*data) & 0x00ff) | (d->baud_divisor & 0xff00); } break; /* Interrupt Enable Register (IER) */ case 0x01: case 0x09: if (d->div_latch == 0) { if (op_type == MTS_READ) { *data = d->channel[channel].ier; } else { d->channel[channel].ier = *data & 0xFF; if ((*data & 0x02) == 0) { /* transmit holding register */ d->channel[channel].vtty->managed_flush = TRUE; vtty_flush(d->channel[channel].vtty); } } } else { if (op_type == MTS_WRITE) d->baud_divisor = (((*data) & 0xff)<<8)|(d->baud_divisor & 0xff); } break; /* Interrupt Ident Register (IIR) */ case 0x02: case 0x0A: if (d->div_latch == 0) { vm_clear_irq(d->vm,d->irq); if (op_type == MTS_READ) { odata = IIR_NPENDING; if (vtty_is_char_avail(d->channel[channel].vtty)) { odata = IIR_RXRDY; } else { if (d->channel[channel].output) { odata = IIR_TXRDY; d->channel[channel].output = 0; } } *data = odata; } } break; case 0x03: case 0x0B: if (op_type == MTS_READ) { *data = d->line_control_reg; } else { d->line_control_reg = (uint)*data; uint bits = 5; __maybe_unused char *stop = "1"; __maybe_unused char *parity = "no "; __maybe_unused char *parityeven = "odd"; if (*data & LCR_WRL0) bits+=1; if (*data & LCR_WRL1) bits+=2; if (*data & LCR_NUMSTOP) { if ( bits >= 6) { stop = "2"; } else { stop = "1.5"; } } if (*data & LCR_PARITYON) parity=""; //Parity on if (*data & LCR_PARITYEV) parityeven="even"; // DIV LATCH changes the behavior of 0x0,0x1,and 0x2 if (*data & LCR_DIVLATCH) { d->div_latch = 1; } else { __maybe_unused uint baud; d->div_latch = 0; // 1200 divisor was 192 // 9600 divisor was 24 // 19200 divisor was 12 // Suggests a crystal of 3686400 hz if (d->baud_divisor > 0) { baud = 3686400 / (d->baud_divisor * 16); } else { baud = 0; } } } break; case 0x04: case 0x0C: if (op_type != MTS_READ) { __maybe_unused char *f1 = ""; __maybe_unused char *f2 = ""; __maybe_unused char *f3 = ""; __maybe_unused char *f4 = ""; __maybe_unused char *f5 = ""; if (*data & MCR_DTR) f1 = "DTR "; if (*data & MCR_RTS) f2 = "RTS "; if (*data & MCR_OUT1) f3 = "OUT1 "; if (*data & MCR_OUT2) f4 = "OUT2 "; if (*data & MCR_LOOP) f5 = "LOOP "; } break; /* Line Status Register (LSR) */ case 0x05: case 0x0D: if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(d->channel[channel].vtty)) odata |= LSR_RXRDY; odata |= LSR_TXRDY|LSR_TXEMPTY; *data = odata; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu, "NS16552","write to addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; }
void *dev_pic32_uart_access (cpu_mips_t * cpu, struct vdevice *dev, m_uint32_t offset, u_int op_size, u_int op_type, m_reg_t * data, m_uint8_t * has_set_value) { struct pic32_uart_data *d = dev->priv_data; unsigned newval; if (offset >= UART_REG_SIZE) { *data = 0; return NULL; } if (op_type == MTS_READ) { /* * Reading UART registers. */ switch (offset) { case PIC32_U1RXREG & 0xff: /* Receive data */ *data = vtty_get_char (d->vtty); if (vtty_is_char_avail (d->vtty)) { d->sta |= PIC32_USTA_URXDA; } else { d->sta &= ~PIC32_USTA_URXDA; d->vm->clear_irq (d->vm, d->irq + IRQ_RX); } break; case PIC32_U1BRG & 0xff: /* Baud rate */ *data = d->brg; break; case PIC32_U1MODE & 0xff: /* Mode */ *data = d->mode; break; case PIC32_U1STA & 0xff: /* Status and control */ d->sta |= PIC32_USTA_RIDLE | /* Receiver is idle */ PIC32_USTA_TRMT; /* Transmit shift register is empty */ if (vtty_is_char_avail (d->vtty)) d->sta |= PIC32_USTA_URXDA; *data = d->sta; #if 0 printf ("<%x>", d->sta); fflush (stdout); #endif break; case PIC32_U1TXREG & 0xff: /* Transmit */ case PIC32_U1MODECLR & 0xff: case PIC32_U1MODESET & 0xff: case PIC32_U1MODEINV & 0xff: case PIC32_U1STACLR & 0xff: case PIC32_U1STASET & 0xff: case PIC32_U1STAINV & 0xff: case PIC32_U1BRGCLR & 0xff: case PIC32_U1BRGSET & 0xff: case PIC32_U1BRGINV & 0xff: *data = 0; break; default: ASSERT (0, "reading unknown uart offset %x\n", offset); } *has_set_value = TRUE; #if 0 printf ("--- uart: read %02x -> %08x\n", offset, *data); fflush (stdout); #endif } else { /* * Writing UART registers. */ #if 0 printf ("--- uart: write %02x := %08x\n", offset, *data); fflush (stdout); #endif switch (offset) { case PIC32_U1TXREG & 0xff: /* Transmit */ /* Skip ^M. */ if ((char) (*data) != '\r') vtty_put_char (d->vtty, (char) (*data)); if ((d->mode & PIC32_UMODE_ON) && (d->sta & PIC32_USTA_UTXEN) && (d->output == 0)) { /* * yajin. * * In order to put the next data more quickly, * just set irq not waiting for * host_alarm_handler to set irq. Sorry uart, * too much work for you. * * Sometimes, linux kernel prints "serial8250: * too much work for irq9" if we print large * data on screen. Please patch the kernel. * comment "printk(KERN_ERR "serial8250: too * much work for " "irq%d\n", irq);" qemu has * some question. * http://lkml.org/lkml/2008/1/12/135 * http://kerneltrap.org/mailarchive/linux-ker * nel/2008/2/7/769924 * * If jit is used in future, we may not need to * set irq here because simulation is quick * enough. Then we have no "too much work for * irq9" problem. */ d->output = TRUE; d->vm->set_irq (d->vm, d->irq + IRQ_TX); } break; case PIC32_U1MODE & 0xff: /* Mode */ newval = *data; write_mode: d->mode = newval; if (!(d->mode & PIC32_UMODE_ON)) { d->vm->clear_irq (d->vm, d->irq + IRQ_RX); d->vm->clear_irq (d->vm, d->irq + IRQ_TX); d->sta &= ~PIC32_USTA_URXDA; d->sta &= ~(PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR | PIC32_USTA_UTXBF); d->sta |= PIC32_USTA_RIDLE | PIC32_USTA_TRMT; } break; case PIC32_U1MODECLR & 0xff: newval = d->mode & ~*data; goto write_mode; case PIC32_U1MODESET & 0xff: newval = d->mode | *data; goto write_mode; case PIC32_U1MODEINV & 0xff: newval = d->mode ^ *data; goto write_mode; case PIC32_U1STA & 0xff: /* Status and control */ newval = *data; write_sta: d->sta &= PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR | PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF; d->sta |= newval & ~(PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR | PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF); if (!(d->sta & PIC32_USTA_URXEN)) { d->vm->clear_irq (d->vm, d->irq + IRQ_RX); d->sta &= ~(PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR); } if (!(d->sta & PIC32_USTA_UTXEN)) { d->vm->clear_irq (d->vm, d->irq + IRQ_TX); d->sta &= ~PIC32_USTA_UTXBF; d->sta |= PIC32_USTA_TRMT; } break; case PIC32_U1STACLR & 0xff: newval = d->sta & ~*data; goto write_sta; case PIC32_U1STASET & 0xff: newval = d->sta | *data; goto write_sta; case PIC32_U1STAINV & 0xff: newval = d->sta ^ *data; goto write_sta; case PIC32_U1BRG & 0xff: /* Baud rate */ newval = *data; write_brg: d->brg = newval; break; case PIC32_U1BRGCLR & 0xff: newval = d->brg & ~*data; goto write_brg; case PIC32_U1BRGSET & 0xff: newval = d->brg | *data; goto write_brg; case PIC32_U1BRGINV & 0xff: newval = d->brg ^ *data; goto write_brg; case PIC32_U1RXREG & 0xff: /* Receive */ /* Ignore */ break; default: ASSERT (0, "writing unknown uart offset %x\n", offset); } *has_set_value = TRUE; } return NULL; }
/* * dev_remote_control_access() */ void *dev_remote_control_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { vm_instance_t *vm = cpu->vm; struct remote_data *d = dev->priv_data; struct vdevice *storage_dev; size_t len; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"REMOTE","reading reg 0x%x at pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"REMOTE","writing reg 0x%x at pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { /* ROM Identification tag */ case 0x000: if (op_type == MTS_READ) *data = ROM_ID; break; /* CPU ID */ case 0x004: if (op_type == MTS_READ) *data = cpu->id; break; /* Display CPU registers */ case 0x008: if (op_type == MTS_WRITE) cpu->reg_dump(cpu); break; /* Display CPU memory info */ case 0x00c: if (op_type == MTS_WRITE) cpu->mmu_dump(cpu); break; /* Reserved/Unused */ case 0x010: break; /* RAM size */ case 0x014: if (op_type == MTS_READ) *data = vm->ram_size; break; /* ROM size */ case 0x018: if (op_type == MTS_READ) *data = vm->rom_size; break; /* NVRAM size */ case 0x01c: if (op_type == MTS_READ) *data = vm->nvram_size; break; /* IOMEM size */ case 0x020: if (op_type == MTS_READ) *data = vm->iomem_size; break; /* Config Register */ case 0x024: if (op_type == MTS_READ) *data = vm->conf_reg; break; /* ELF entry point */ case 0x028: if (op_type == MTS_READ) *data = vm->ios_entry_point; break; /* ELF machine id */ case 0x02c: if (op_type == MTS_READ) *data = vm->elf_machine_id; break; /* Restart IOS Image */ case 0x030: /* not implemented */ break; /* Stop the virtual machine */ case 0x034: // FIXME: WTF is this for?!?!? //vm->status = VM_STATUS_SHUTDOWN; break; /* Debugging/Log message: /!\ physical address */ case 0x038: if (op_type == MTS_WRITE) { len = physmem_strlen(vm,*data); if (len < sizeof(d->con_buffer)) { physmem_copy_from_vm(vm,d->con_buffer,*data,len+1); vm_log(vm,"ROM",d->con_buffer); } } break; /* Console Buffering */ case 0x03c: if (op_type == MTS_WRITE) { if (d->con_buf_pos < (sizeof(d->con_buffer)-1)) { d->con_buffer[d->con_buf_pos++] = *data & 0xFF; d->con_buffer[d->con_buf_pos] = 0; if (d->con_buffer[d->con_buf_pos-1] == '\n') { vm_log(vm,"ROM","%s",d->con_buffer); d->con_buf_pos = 0; } } else d->con_buf_pos = 0; } break; /* Console output */ case 0x040: if (op_type == MTS_WRITE) vtty_put_char(vm->vtty_con,(char)*data); break; /* NVRAM address */ case 0x044: if (op_type == MTS_READ) { if ((storage_dev = dev_get_by_name(vm,"nvram"))) *data = storage_dev->phys_addr; if ((storage_dev = dev_get_by_name(vm,"ssa"))) *data = storage_dev->phys_addr; if (cpu->type == CPU_TYPE_MIPS64) *data += MIPS_KSEG1_BASE; } break; /* IO memory size for Smart-Init (C3600, others ?) */ case 0x048: if (op_type == MTS_READ) *data = vm->nm_iomem_size; break; /* Cookie position selector */ case 0x04c: if (op_type == MTS_READ) *data = d->cookie_pos; else d->cookie_pos = *data; break; /* Cookie data */ case 0x050: if ((op_type == MTS_READ) && (d->cookie_pos < 64)) *data = vm->chassis_cookie[d->cookie_pos]; break; /* ROMMON variable */ case 0x054: if (op_type == MTS_WRITE) { if (d->var_buf_pos < (sizeof(d->var_buffer)-1)) { d->var_buffer[d->var_buf_pos++] = *data & 0xFF; d->var_buffer[d->var_buf_pos] = 0; } else d->var_buf_pos = 0; } else { if (d->var_buf_pos < (sizeof(d->var_buffer)-1)) { *data = d->var_buffer[d->var_buf_pos++]; } else { d->var_buf_pos = 0; *data = 0; } } break; /* ROMMON variable command */ case 0x058: if (op_type == MTS_WRITE) { switch(*data & 0xFF) { case ROMMON_SET_VAR: d->var_status = rommon_var_add_str(&vm->rommon_vars, d->var_buffer); d->var_buf_pos = 0; break; case ROMMON_GET_VAR: d->var_status = rommon_var_get(&vm->rommon_vars, d->var_buffer, d->var_buffer, sizeof(d->var_buffer)); d->var_buf_pos = 0; break; case ROMMON_CLEAR_VAR_STAT: d->var_buf_pos = 0; break; default: d->var_status = -1; } } else { *data = d->var_status; } break; } return NULL; }