| /* |
| * QEMU System Emulator |
| * |
| * Copyright (c) 2003-2008 Fabrice Bellard |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| /* the following is needed on Linux to define ptsname() in stdlib.h */ |
| #if defined(__linux__) |
| #define _GNU_SOURCE 1 |
| #endif |
| |
| #include "qemu-common.h" |
| #include "hw/hw.h" |
| #include "hw/boards.h" |
| #include "hw/usb.h" |
| #include "hw/pcmcia.h" |
| #include "hw/pc.h" |
| #include "hw/audiodev.h" |
| #include "hw/isa.h" |
| #include "hw/baum.h" |
| #include "net.h" |
| #include "console.h" |
| #include "sysemu.h" |
| #include "gdbstub.h" |
| #include "qemu-timer.h" |
| #include "qemu-char.h" |
| #include "block.h" |
| #include "audio/audio.h" |
| |
| #include "qemu_file.h" |
| #include "android/android.h" |
| #include "charpipe.h" |
| #include "shaper.h" |
| #include "modem_driver.h" |
| #include "android/gps.h" |
| #include "android/hw-qemud.h" |
| #include "android/hw-kmsg.h" |
| #include "tcpdump.h" |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <sys/time.h> |
| #include <zlib.h> |
| |
| #ifndef _WIN32 |
| #include <sys/times.h> |
| #include <sys/wait.h> |
| #include <termios.h> |
| #include <sys/poll.h> |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <dirent.h> |
| #include <netdb.h> |
| #include <sys/select.h> |
| #include <arpa/inet.h> |
| #ifdef _BSD |
| #include <sys/stat.h> |
| #if !defined(__APPLE__) && !defined(__OpenBSD__) |
| #include <libutil.h> |
| #endif |
| #ifdef __OpenBSD__ |
| #include <net/if.h> |
| #endif |
| #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) |
| #include <freebsd/stdlib.h> |
| #else |
| #ifndef __sun__ |
| #include <linux/if.h> |
| #include <linux/if_tun.h> |
| #include <pty.h> |
| #include <malloc.h> |
| #include <linux/rtc.h> |
| |
| /* For the benefit of older linux systems which don't supply it, |
| we use a local copy of hpet.h. */ |
| /* #include <linux/hpet.h> */ |
| #include "hpet.h" |
| |
| #include <linux/ppdev.h> |
| #include <linux/parport.h> |
| #else |
| #include <sys/stat.h> |
| #include <sys/ethernet.h> |
| #include <sys/sockio.h> |
| #include <netinet/arp.h> |
| #include <netinet/in.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #include <netinet/ip_icmp.h> // must come after ip.h |
| #include <netinet/udp.h> |
| #include <netinet/tcp.h> |
| #include <net/if.h> |
| #include <syslog.h> |
| #include <stropts.h> |
| #endif |
| #endif |
| #endif |
| |
| #include "qemu_socket.h" |
| |
| #if defined(CONFIG_SLIRP) |
| #include "libslirp.h" |
| #endif |
| |
| #if defined(__OpenBSD__) |
| #include <util.h> |
| #endif |
| |
| #if defined(CONFIG_VDE) |
| #include <libvdeplug.h> |
| #endif |
| |
| #ifdef _WIN32 |
| #include <malloc.h> |
| #include <sys/timeb.h> |
| #include <mmsystem.h> |
| #define getopt_long_only getopt_long |
| #define memalign(align, size) malloc(size) |
| #endif |
| |
| |
| #ifdef CONFIG_COCOA |
| #undef main |
| #define main qemu_main |
| #endif /* CONFIG_COCOA */ |
| |
| #ifdef CONFIG_SKINS |
| #undef main |
| #define main qemu_main |
| #endif |
| |
| #include "disas.h" |
| |
| #include "exec-all.h" |
| |
| #ifdef CONFIG_TRACE |
| #include "trace.h" |
| #include "dcache.h" |
| #endif |
| |
| #ifdef CONFIG_NAND |
| #include "hw/goldfish_nand.h" |
| #endif |
| |
| #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" |
| #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" |
| #ifdef __sun__ |
| #define SMBD_COMMAND "/usr/sfw/sbin/smbd" |
| #else |
| #define SMBD_COMMAND "/usr/sbin/smbd" |
| #endif |
| |
| //#define DEBUG_UNUSED_IOPORT |
| //#define DEBUG_IOPORT |
| |
| #ifdef TARGET_PPC |
| #define DEFAULT_RAM_SIZE 144 |
| #else |
| #define DEFAULT_RAM_SIZE 128 |
| #endif |
| |
| /* Max number of USB devices that can be specified on the commandline. */ |
| #define MAX_USB_CMDLINE 8 |
| |
| /* XXX: use a two level table to limit memory usage */ |
| #define MAX_IOPORTS 65536 |
| |
| const char *bios_dir = CONFIG_QEMU_SHAREDIR; |
| const char *bios_name = NULL; |
| void *ioport_opaque[MAX_IOPORTS]; |
| IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; |
| IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; |
| /* Note: drives_table[MAX_DRIVES] is a dummy block driver if none available |
| to store the VM snapshots */ |
| DriveInfo drives_table[MAX_DRIVES+1]; |
| int nb_drives; |
| /* point to the block driver where the snapshots are managed */ |
| BlockDriverState *bs_snapshots; |
| int vga_ram_size; |
| static DisplayState display_state; |
| int nographic; |
| int curses; |
| const char* keyboard_layout = NULL; |
| int64_t ticks_per_sec; |
| ram_addr_t ram_size; |
| int pit_min_timer_count = 0; |
| int nb_nics; |
| NICInfo nd_table[MAX_NICS]; |
| int vm_running; |
| static int rtc_utc = 1; |
| static int rtc_date_offset = -1; /* -1 means no change */ |
| int cirrus_vga_enabled = 1; |
| int vmsvga_enabled = 0; |
| #ifdef TARGET_SPARC |
| int graphic_width = 1024; |
| int graphic_height = 768; |
| int graphic_depth = 8; |
| #else |
| int graphic_width = 800; |
| int graphic_height = 600; |
| int graphic_depth = 15; |
| #endif |
| int full_screen = 0; |
| int no_frame = 0; |
| int no_quit = 0; |
| CharDriverState *serial_hds[MAX_SERIAL_PORTS]; |
| CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; |
| #ifdef TARGET_I386 |
| int win2k_install_hack = 0; |
| #endif |
| int usb_enabled = 0; |
| static VLANState *first_vlan; |
| int smp_cpus = 1; |
| const char *vnc_display; |
| #if defined(TARGET_SPARC) |
| #define MAX_CPUS 16 |
| #elif defined(TARGET_I386) |
| #define MAX_CPUS 255 |
| #else |
| #define MAX_CPUS 1 |
| #endif |
| int acpi_enabled = 1; |
| int fd_bootchk = 1; |
| int no_reboot = 0; |
| int no_shutdown = 0; |
| int cursor_hide = 1; |
| int graphic_rotate = 0; |
| int daemonize = 0; |
| const char *option_rom[MAX_OPTION_ROMS]; |
| int nb_option_roms; |
| int semihosting_enabled = 0; |
| int autostart = 1; |
| #ifdef TARGET_ARM |
| int old_param = 0; |
| #endif |
| const char *qemu_name; |
| int alt_grab = 0; |
| #ifdef TARGET_SPARC |
| unsigned int nb_prom_envs = 0; |
| const char *prom_envs[MAX_PROM_ENVS]; |
| #endif |
| int nb_drives_opt; |
| struct drive_opt { |
| const char *file; |
| char opt[1024]; |
| } drives_opt[MAX_DRIVES]; |
| |
| static CPUState *cur_cpu; |
| static CPUState *next_cpu; |
| static int event_pending = 1; |
| /* Conversion factor from emulated instructions to virtual clock ticks. */ |
| static int icount_time_shift; |
| /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ |
| #define MAX_ICOUNT_SHIFT 10 |
| /* Compensate for varying guest execution speed. */ |
| static int64_t qemu_icount_bias; |
| QEMUTimer *icount_rt_timer; |
| QEMUTimer *icount_vm_timer; |
| |
| |
| extern int qemu_cpu_delay; |
| extern char* audio_input_source; |
| |
| extern void dprint( const char* format, ... ); |
| |
| #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) |
| |
| /***********************************************************/ |
| /* x86 ISA bus support */ |
| |
| target_phys_addr_t isa_mem_base = 0; |
| PicState2 *isa_pic; |
| |
| static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl; |
| static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel; |
| |
| static uint32_t ioport_read(int index, uint32_t address) |
| { |
| static IOPortReadFunc *default_func[3] = { |
| default_ioport_readb, |
| default_ioport_readw, |
| default_ioport_readl |
| }; |
| IOPortReadFunc *func = ioport_read_table[index][address]; |
| if (!func) |
| func = default_func[index]; |
| return func(ioport_opaque[address], address); |
| } |
| |
| static void ioport_write(int index, uint32_t address, uint32_t data) |
| { |
| static IOPortWriteFunc *default_func[3] = { |
| default_ioport_writeb, |
| default_ioport_writew, |
| default_ioport_writel |
| }; |
| IOPortWriteFunc *func = ioport_write_table[index][address]; |
| if (!func) |
| func = default_func[index]; |
| func(ioport_opaque[address], address, data); |
| } |
| |
| static uint32_t default_ioport_readb(void *opaque, uint32_t address) |
| { |
| #ifdef DEBUG_UNUSED_IOPORT |
| fprintf(stderr, "unused inb: port=0x%04x\n", address); |
| #endif |
| return 0xff; |
| } |
| |
| static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data) |
| { |
| #ifdef DEBUG_UNUSED_IOPORT |
| fprintf(stderr, "unused outb: port=0x%04x data=0x%02x\n", address, data); |
| #endif |
| } |
| |
| /* default is to make two byte accesses */ |
| static uint32_t default_ioport_readw(void *opaque, uint32_t address) |
| { |
| uint32_t data; |
| data = ioport_read(0, address); |
| address = (address + 1) & (MAX_IOPORTS - 1); |
| data |= ioport_read(0, address) << 8; |
| return data; |
| } |
| |
| static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data) |
| { |
| ioport_write(0, address, data & 0xff); |
| address = (address + 1) & (MAX_IOPORTS - 1); |
| ioport_write(0, address, (data >> 8) & 0xff); |
| } |
| |
| static uint32_t default_ioport_readl(void *opaque, uint32_t address) |
| { |
| #ifdef DEBUG_UNUSED_IOPORT |
| fprintf(stderr, "unused inl: port=0x%04x\n", address); |
| #endif |
| return 0xffffffff; |
| } |
| |
| static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) |
| { |
| #ifdef DEBUG_UNUSED_IOPORT |
| fprintf(stderr, "unused outl: port=0x%04x data=0x%02x\n", address, data); |
| #endif |
| } |
| |
| /* size is the word size in byte */ |
| int register_ioport_read(int start, int length, int size, |
| IOPortReadFunc *func, void *opaque) |
| { |
| int i, bsize; |
| |
| if (size == 1) { |
| bsize = 0; |
| } else if (size == 2) { |
| bsize = 1; |
| } else if (size == 4) { |
| bsize = 2; |
| } else { |
| hw_error("register_ioport_read: invalid size"); |
| return -1; |
| } |
| for(i = start; i < start + length; i += size) { |
| ioport_read_table[bsize][i] = func; |
| if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) |
| hw_error("register_ioport_read: invalid opaque"); |
| ioport_opaque[i] = opaque; |
| } |
| return 0; |
| } |
| |
| /* size is the word size in byte */ |
| int register_ioport_write(int start, int length, int size, |
| IOPortWriteFunc *func, void *opaque) |
| { |
| int i, bsize; |
| |
| if (size == 1) { |
| bsize = 0; |
| } else if (size == 2) { |
| bsize = 1; |
| } else if (size == 4) { |
| bsize = 2; |
| } else { |
| hw_error("register_ioport_write: invalid size"); |
| return -1; |
| } |
| for(i = start; i < start + length; i += size) { |
| ioport_write_table[bsize][i] = func; |
| if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) |
| hw_error("register_ioport_write: invalid opaque"); |
| ioport_opaque[i] = opaque; |
| } |
| return 0; |
| } |
| |
| void isa_unassign_ioport(int start, int length) |
| { |
| int i; |
| |
| for(i = start; i < start + length; i++) { |
| ioport_read_table[0][i] = default_ioport_readb; |
| ioport_read_table[1][i] = default_ioport_readw; |
| ioport_read_table[2][i] = default_ioport_readl; |
| |
| ioport_write_table[0][i] = default_ioport_writeb; |
| ioport_write_table[1][i] = default_ioport_writew; |
| ioport_write_table[2][i] = default_ioport_writel; |
| } |
| } |
| |
| /***********************************************************/ |
| |
| void cpu_outb(CPUState *env, int addr, int val) |
| { |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "outb: %04x %02x\n", addr, val); |
| #endif |
| ioport_write(0, addr, val); |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| } |
| |
| void cpu_outw(CPUState *env, int addr, int val) |
| { |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "outw: %04x %04x\n", addr, val); |
| #endif |
| ioport_write(1, addr, val); |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| } |
| |
| void cpu_outl(CPUState *env, int addr, int val) |
| { |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "outl: %04x %08x\n", addr, val); |
| #endif |
| ioport_write(2, addr, val); |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| } |
| |
| int cpu_inb(CPUState *env, int addr) |
| { |
| int val; |
| val = ioport_read(0, addr); |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "inb : %04x %02x\n", addr, val); |
| #endif |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| return val; |
| } |
| |
| int cpu_inw(CPUState *env, int addr) |
| { |
| int val; |
| val = ioport_read(1, addr); |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "inw : %04x %04x\n", addr, val); |
| #endif |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| return val; |
| } |
| |
| int cpu_inl(CPUState *env, int addr) |
| { |
| int val; |
| val = ioport_read(2, addr); |
| #ifdef DEBUG_IOPORT |
| if (loglevel & CPU_LOG_IOPORT) |
| fprintf(logfile, "inl : %04x %08x\n", addr, val); |
| #endif |
| #ifdef USE_KQEMU |
| if (env) |
| env->last_io_time = cpu_get_time_fast(); |
| #endif |
| return val; |
| } |
| |
| /***********************************************************/ |
| void hw_error(const char *fmt, ...) |
| { |
| va_list ap; |
| CPUState *env; |
| |
| va_start(ap, fmt); |
| fprintf(stderr, "qemu: hardware error: "); |
| vfprintf(stderr, fmt, ap); |
| fprintf(stderr, "\n"); |
| for(env = first_cpu; env != NULL; env = env->next_cpu) { |
| fprintf(stderr, "CPU #%d:\n", env->cpu_index); |
| #ifdef TARGET_I386 |
| cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); |
| #else |
| cpu_dump_state(env, stderr, fprintf, 0); |
| #endif |
| } |
| va_end(ap); |
| abort(); |
| } |
| |
| /***********************************************************/ |
| /* keyboard/mouse */ |
| |
| static QEMUPutKBDEvent* qemu_put_kbd_event; |
| static void* qemu_put_kbd_event_opaque; |
| |
| static QEMUPutKBDEventN* qemu_put_kbd_event_n; |
| static void* qemu_put_kbd_event_n_opaque; |
| |
| |
| static QEMUPutGenericEvent* qemu_put_generic_event; |
| static void* qemu_put_generic_event_opaque; |
| |
| static QEMUPutMouseEntry *qemu_put_mouse_event_head; |
| static QEMUPutMouseEntry *qemu_put_mouse_event_current; |
| |
| void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) |
| { |
| qemu_put_kbd_event_opaque = opaque; |
| qemu_put_kbd_event = func; |
| } |
| |
| void qemu_add_kbd_event_n_handler(QEMUPutKBDEventN *func, void *opaque) |
| { |
| qemu_put_kbd_event_n_opaque = opaque; |
| qemu_put_kbd_event_n = func; |
| } |
| |
| #if 0 |
| void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute) |
| { |
| qemu_put_mouse_event_opaque = opaque; |
| qemu_put_mouse_event = func; |
| qemu_put_mouse_event_absolute = absolute; |
| } |
| #else |
| QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, |
| void *opaque, int absolute, |
| const char *name) |
| { |
| QEMUPutMouseEntry *s, *cursor; |
| |
| s = qemu_mallocz(sizeof(QEMUPutMouseEntry)); |
| if (!s) |
| return NULL; |
| |
| s->qemu_put_mouse_event = func; |
| s->qemu_put_mouse_event_opaque = opaque; |
| s->qemu_put_mouse_event_absolute = absolute; |
| s->qemu_put_mouse_event_name = qemu_strdup(name); |
| s->next = NULL; |
| |
| if (!qemu_put_mouse_event_head) { |
| qemu_put_mouse_event_head = qemu_put_mouse_event_current = s; |
| return s; |
| } |
| |
| cursor = qemu_put_mouse_event_head; |
| while (cursor->next != NULL) |
| cursor = cursor->next; |
| |
| cursor->next = s; |
| qemu_put_mouse_event_current = s; |
| |
| return s; |
| } |
| |
| void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) |
| { |
| QEMUPutMouseEntry *prev = NULL, *cursor; |
| |
| if (!qemu_put_mouse_event_head || entry == NULL) |
| return; |
| |
| cursor = qemu_put_mouse_event_head; |
| while (cursor != NULL && cursor != entry) { |
| prev = cursor; |
| cursor = cursor->next; |
| } |
| |
| if (cursor == NULL) // does not exist or list empty |
| return; |
| else if (prev == NULL) { // entry is head |
| qemu_put_mouse_event_head = cursor->next; |
| if (qemu_put_mouse_event_current == entry) |
| qemu_put_mouse_event_current = cursor->next; |
| qemu_free(entry->qemu_put_mouse_event_name); |
| qemu_free(entry); |
| return; |
| } |
| |
| prev->next = entry->next; |
| |
| if (qemu_put_mouse_event_current == entry) |
| qemu_put_mouse_event_current = prev; |
| |
| qemu_free(entry->qemu_put_mouse_event_name); |
| qemu_free(entry); |
| } |
| #endif |
| |
| void qemu_add_generic_event_handler(QEMUPutGenericEvent *func, void* opaque) |
| { |
| qemu_put_generic_event = func; |
| qemu_put_generic_event_opaque = opaque; |
| } |
| |
| void kbd_put_keycode(int keycode) |
| { |
| if (qemu_put_kbd_event) { |
| qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); |
| } |
| } |
| |
| void kbd_put_keycodes(int* keycodes, int count) |
| { |
| if (qemu_put_kbd_event_n) |
| { |
| qemu_put_kbd_event_n(qemu_put_kbd_event_n_opaque, keycodes, count); |
| } |
| else if (qemu_put_kbd_event) |
| { |
| int nn; |
| |
| for (nn = 0; nn < count; nn++) |
| qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycodes[nn]); |
| } |
| } |
| |
| |
| void kbd_generic_event(int type, int code, int value) |
| { |
| if (qemu_put_generic_event) |
| qemu_put_generic_event(qemu_put_generic_event_opaque, type, code, value); |
| } |
| |
| |
| void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) |
| { |
| QEMUPutMouseEvent *mouse_event; |
| void *mouse_event_opaque; |
| int width; |
| |
| if (!qemu_put_mouse_event_current) { |
| return; |
| } |
| |
| mouse_event = |
| qemu_put_mouse_event_current->qemu_put_mouse_event; |
| mouse_event_opaque = |
| qemu_put_mouse_event_current->qemu_put_mouse_event_opaque; |
| |
| if (mouse_event) { |
| if (graphic_rotate) { |
| if (qemu_put_mouse_event_current->qemu_put_mouse_event_absolute) |
| width = 0x7fff; |
| else |
| width = graphic_width - 1; |
| mouse_event(mouse_event_opaque, |
| width - dy, dx, dz, buttons_state); |
| } else |
| mouse_event(mouse_event_opaque, |
| dx, dy, dz, buttons_state); |
| } |
| } |
| |
| int kbd_mouse_is_absolute(void) |
| { |
| if (!qemu_put_mouse_event_current) |
| return 0; |
| |
| return qemu_put_mouse_event_current->qemu_put_mouse_event_absolute; |
| } |
| |
| void do_info_mice(void) |
| { |
| QEMUPutMouseEntry *cursor; |
| int index = 0; |
| |
| if (!qemu_put_mouse_event_head) { |
| term_printf("No mouse devices connected\n"); |
| return; |
| } |
| |
| term_printf("Mouse devices available:\n"); |
| cursor = qemu_put_mouse_event_head; |
| while (cursor != NULL) { |
| term_printf("%c Mouse #%d: %s\n", |
| (cursor == qemu_put_mouse_event_current ? '*' : ' '), |
| index, cursor->qemu_put_mouse_event_name); |
| index++; |
| cursor = cursor->next; |
| } |
| } |
| |
| void do_mouse_set(int index) |
| { |
| QEMUPutMouseEntry *cursor; |
| int i = 0; |
| |
| if (!qemu_put_mouse_event_head) { |
| term_printf("No mouse devices connected\n"); |
| return; |
| } |
| |
| cursor = qemu_put_mouse_event_head; |
| while (cursor != NULL && index != i) { |
| i++; |
| cursor = cursor->next; |
| } |
| |
| if (cursor != NULL) |
| qemu_put_mouse_event_current = cursor; |
| else |
| term_printf("Mouse at given index not found\n"); |
| } |
| |
| /* compute with 96 bit intermediate result: (a*b)/c */ |
| uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) |
| { |
| union { |
| uint64_t ll; |
| struct { |
| #ifdef WORDS_BIGENDIAN |
| uint32_t high, low; |
| #else |
| uint32_t low, high; |
| #endif |
| } l; |
| } u, res; |
| uint64_t rl, rh; |
| |
| u.ll = a; |
| rl = (uint64_t)u.l.low * (uint64_t)b; |
| rh = (uint64_t)u.l.high * (uint64_t)b; |
| rh += (rl >> 32); |
| res.l.high = rh / c; |
| res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; |
| return res.ll; |
| } |
| |
| /***********************************************************/ |
| /* real time host monotonic timer */ |
| |
| #define QEMU_TIMER_BASE 1000000000LL |
| |
| #ifdef WIN32 |
| |
| static int64_t clock_freq; |
| |
| static void init_get_clock(void) |
| { |
| LARGE_INTEGER freq; |
| int ret; |
| ret = QueryPerformanceFrequency(&freq); |
| if (ret == 0) { |
| fprintf(stderr, "Could not calibrate ticks\n"); |
| exit(1); |
| } |
| clock_freq = freq.QuadPart; |
| } |
| |
| static int64_t get_clock(void) |
| { |
| LARGE_INTEGER ti; |
| QueryPerformanceCounter(&ti); |
| return muldiv64(ti.QuadPart, QEMU_TIMER_BASE, clock_freq); |
| } |
| |
| #else |
| |
| static int use_rt_clock; |
| |
| static void init_get_clock(void) |
| { |
| use_rt_clock = 0; |
| #if defined(__linux__) |
| { |
| struct timespec ts; |
| if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { |
| use_rt_clock = 1; |
| } |
| } |
| #endif |
| } |
| |
| static int64_t get_clock(void) |
| { |
| #if defined(__linux__) |
| if (use_rt_clock) { |
| struct timespec ts; |
| clock_gettime(CLOCK_MONOTONIC, &ts); |
| return ts.tv_sec * 1000000000LL + ts.tv_nsec; |
| } else |
| #endif |
| { |
| /* XXX: using gettimeofday leads to problems if the date |
| changes, so it should be avoided. */ |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); |
| } |
| } |
| #endif |
| |
| /* Return the virtual CPU time, based on the instruction counter. */ |
| static int64_t cpu_get_icount(void) |
| { |
| int64_t icount; |
| CPUState *env = cpu_single_env;; |
| icount = qemu_icount; |
| if (env) { |
| if (!can_do_io(env)) |
| fprintf(stderr, "Bad clock read\n"); |
| icount -= (env->icount_decr.u16.low + env->icount_extra); |
| } |
| return qemu_icount_bias + (icount << icount_time_shift); |
| } |
| |
| /***********************************************************/ |
| /* guest cycle counter */ |
| |
| static int64_t cpu_ticks_prev; |
| static int64_t cpu_ticks_offset; |
| static int64_t cpu_clock_offset; |
| static int cpu_ticks_enabled; |
| |
| /* return the host CPU cycle counter and handle stop/restart */ |
| int64_t cpu_get_ticks(void) |
| { |
| if (use_icount) { |
| return cpu_get_icount(); |
| } |
| if (!cpu_ticks_enabled) { |
| return cpu_ticks_offset; |
| } else { |
| int64_t ticks; |
| ticks = cpu_get_real_ticks(); |
| if (cpu_ticks_prev > ticks) { |
| /* Note: non increasing ticks may happen if the host uses |
| software suspend */ |
| cpu_ticks_offset += cpu_ticks_prev - ticks; |
| } |
| cpu_ticks_prev = ticks; |
| return ticks + cpu_ticks_offset; |
| } |
| } |
| |
| /* return the host CPU monotonic timer and handle stop/restart */ |
| static int64_t cpu_get_clock(void) |
| { |
| int64_t ti; |
| if (!cpu_ticks_enabled) { |
| return cpu_clock_offset; |
| } else { |
| ti = get_clock(); |
| return ti + cpu_clock_offset; |
| } |
| } |
| |
| /* enable cpu_get_ticks() */ |
| void cpu_enable_ticks(void) |
| { |
| if (!cpu_ticks_enabled) { |
| cpu_ticks_offset -= cpu_get_real_ticks(); |
| cpu_clock_offset -= get_clock(); |
| cpu_ticks_enabled = 1; |
| } |
| } |
| |
| /* disable cpu_get_ticks() : the clock is stopped. You must not call |
| cpu_get_ticks() after that. */ |
| void cpu_disable_ticks(void) |
| { |
| if (cpu_ticks_enabled) { |
| cpu_ticks_offset = cpu_get_ticks(); |
| cpu_clock_offset = cpu_get_clock(); |
| cpu_ticks_enabled = 0; |
| } |
| } |
| |
| /***********************************************************/ |
| /* timers */ |
| |
| #define QEMU_TIMER_REALTIME 0 |
| #define QEMU_TIMER_VIRTUAL 1 |
| |
| struct QEMUClock { |
| int type; |
| /* XXX: add frequency */ |
| }; |
| |
| struct QEMUTimer { |
| QEMUClock *clock; |
| int64_t expire_time; |
| QEMUTimerCB *cb; |
| void *opaque; |
| struct QEMUTimer *next; |
| }; |
| |
| struct qemu_alarm_timer { |
| char const *name; |
| unsigned int flags; |
| |
| int (*start)(struct qemu_alarm_timer *t); |
| void (*stop)(struct qemu_alarm_timer *t); |
| void (*rearm)(struct qemu_alarm_timer *t); |
| void *priv; |
| }; |
| |
| #define ALARM_FLAG_DYNTICKS 0x1 |
| #define ALARM_FLAG_EXPIRED 0x2 |
| |
| static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) |
| { |
| return t->flags & ALARM_FLAG_DYNTICKS; |
| } |
| |
| static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) |
| { |
| if (!alarm_has_dynticks(t)) |
| return; |
| |
| t->rearm(t); |
| } |
| |
| /* TODO: MIN_TIMER_REARM_US should be optimized */ |
| #define MIN_TIMER_REARM_US 250 |
| |
| static struct qemu_alarm_timer *alarm_timer; |
| |
| #ifdef _WIN32 |
| |
| struct qemu_alarm_win32 { |
| MMRESULT timerId; |
| HANDLE host_alarm; |
| unsigned int period; |
| } alarm_win32_data = {0, NULL, -1}; |
| |
| static int win32_start_timer(struct qemu_alarm_timer *t); |
| static void win32_stop_timer(struct qemu_alarm_timer *t); |
| static void win32_rearm_timer(struct qemu_alarm_timer *t); |
| |
| #else |
| |
| static int unix_start_timer(struct qemu_alarm_timer *t); |
| static void unix_stop_timer(struct qemu_alarm_timer *t); |
| |
| #ifdef __linux__ |
| |
| static int dynticks_start_timer(struct qemu_alarm_timer *t); |
| static void dynticks_stop_timer(struct qemu_alarm_timer *t); |
| static void dynticks_rearm_timer(struct qemu_alarm_timer *t); |
| |
| static int hpet_start_timer(struct qemu_alarm_timer *t); |
| static void hpet_stop_timer(struct qemu_alarm_timer *t); |
| |
| static int rtc_start_timer(struct qemu_alarm_timer *t); |
| static void rtc_stop_timer(struct qemu_alarm_timer *t); |
| |
| #endif /* __linux__ */ |
| |
| #endif /* _WIN32 */ |
| |
| /* Correlation between real and virtual time is always going to be |
| fairly approximate, so ignore small variation. |
| When the guest is idle real and virtual time will be aligned in |
| the IO wait loop. */ |
| #define ICOUNT_WOBBLE (QEMU_TIMER_BASE / 10) |
| |
| static void icount_adjust(void) |
| { |
| int64_t cur_time; |
| int64_t cur_icount; |
| int64_t delta; |
| static int64_t last_delta; |
| /* If the VM is not running, then do nothing. */ |
| if (!vm_running) |
| return; |
| |
| cur_time = cpu_get_clock(); |
| cur_icount = qemu_get_clock(vm_clock); |
| delta = cur_icount - cur_time; |
| /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ |
| if (delta > 0 |
| && last_delta + ICOUNT_WOBBLE < delta * 2 |
| && icount_time_shift > 0) { |
| /* The guest is getting too far ahead. Slow time down. */ |
| icount_time_shift--; |
| } |
| if (delta < 0 |
| && last_delta - ICOUNT_WOBBLE > delta * 2 |
| && icount_time_shift < MAX_ICOUNT_SHIFT) { |
| /* The guest is getting too far behind. Speed time up. */ |
| icount_time_shift++; |
| } |
| last_delta = delta; |
| qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift); |
| } |
| |
| static void icount_adjust_rt(void * opaque) |
| { |
| qemu_mod_timer(icount_rt_timer, |
| qemu_get_clock(rt_clock) + 1000); |
| icount_adjust(); |
| } |
| |
| static void icount_adjust_vm(void * opaque) |
| { |
| qemu_mod_timer(icount_vm_timer, |
| qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10); |
| icount_adjust(); |
| } |
| |
| static void init_icount_adjust(void) |
| { |
| /* Have both realtime and virtual time triggers for speed adjustment. |
| The realtime trigger catches emulated time passing too slowly, |
| the virtual time trigger catches emulated time passing too fast. |
| Realtime triggers occur even when idle, so use them less frequently |
| than VM triggers. */ |
| icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL); |
| qemu_mod_timer(icount_rt_timer, |
| qemu_get_clock(rt_clock) + 1000); |
| icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); |
| qemu_mod_timer(icount_vm_timer, |
| qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10); |
| } |
| |
| static struct qemu_alarm_timer alarm_timers[] = { |
| #ifndef _WIN32 |
| #ifdef __linux__ |
| {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, |
| dynticks_stop_timer, dynticks_rearm_timer, NULL}, |
| /* HPET - if available - is preferred */ |
| {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL}, |
| /* ...otherwise try RTC */ |
| {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL}, |
| #endif |
| {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL}, |
| #else |
| {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer, |
| win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, |
| {"win32", 0, win32_start_timer, |
| win32_stop_timer, NULL, &alarm_win32_data}, |
| #endif |
| {NULL, 0, NULL, NULL, NULL, NULL} |
| }; |
| |
| static void show_available_alarms(void) |
| { |
| int i; |
| |
| printf("Available alarm timers, in order of precedence:\n"); |
| for (i = 0; alarm_timers[i].name; i++) |
| printf("%s\n", alarm_timers[i].name); |
| } |
| |
| static void configure_alarms(char const *opt) |
| { |
| int i; |
| int cur = 0; |
| int count = (sizeof(alarm_timers) / sizeof(*alarm_timers)) - 1; |
| char *arg; |
| char *name; |
| struct qemu_alarm_timer tmp; |
| |
| if (!strcmp(opt, "?")) { |
| show_available_alarms(); |
| exit(0); |
| } |
| |
| arg = strdup(opt); |
| |
| /* Reorder the array */ |
| name = strtok(arg, ","); |
| while (name) { |
| for (i = 0; i < count && alarm_timers[i].name; i++) { |
| if (!strcmp(alarm_timers[i].name, name)) |
| break; |
| } |
| |
| if (i == count) { |
| fprintf(stderr, "Unknown clock %s\n", name); |
| goto next; |
| } |
| |
| if (i < cur) |
| /* Ignore */ |
| goto next; |
| |
| /* Swap */ |
| tmp = alarm_timers[i]; |
| alarm_timers[i] = alarm_timers[cur]; |
| alarm_timers[cur] = tmp; |
| |
| cur++; |
| next: |
| name = strtok(NULL, ","); |
| } |
| |
| free(arg); |
| |
| if (cur) { |
| /* Disable remaining timers */ |
| for (i = cur; i < count; i++) |
| alarm_timers[i].name = NULL; |
| } else { |
| show_available_alarms(); |
| exit(1); |
| } |
| } |
| |
| QEMUClock *rt_clock; |
| QEMUClock *vm_clock; |
| |
| static QEMUTimer *active_timers[2]; |
| |
| static QEMUClock *qemu_new_clock(int type) |
| { |
| QEMUClock *clock; |
| clock = qemu_mallocz(sizeof(QEMUClock)); |
| if (!clock) |
| return NULL; |
| clock->type = type; |
| return clock; |
| } |
| |
| QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) |
| { |
| QEMUTimer *ts; |
| |
| ts = qemu_mallocz(sizeof(QEMUTimer)); |
| ts->clock = clock; |
| ts->cb = cb; |
| ts->opaque = opaque; |
| return ts; |
| } |
| |
| void qemu_free_timer(QEMUTimer *ts) |
| { |
| qemu_free(ts); |
| } |
| |
| /* stop a timer, but do not dealloc it */ |
| void qemu_del_timer(QEMUTimer *ts) |
| { |
| QEMUTimer **pt, *t; |
| |
| /* NOTE: this code must be signal safe because |
| qemu_timer_expired() can be called from a signal. */ |
| pt = &active_timers[ts->clock->type]; |
| for(;;) { |
| t = *pt; |
| if (!t) |
| break; |
| if (t == ts) { |
| *pt = t->next; |
| break; |
| } |
| pt = &t->next; |
| } |
| } |
| |
| /* modify the current timer so that it will be fired when current_time |
| >= expire_time. The corresponding callback will be called. */ |
| void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) |
| { |
| QEMUTimer **pt, *t; |
| |
| qemu_del_timer(ts); |
| |
| /* add the timer in the sorted list */ |
| /* NOTE: this code must be signal safe because |
| qemu_timer_expired() can be called from a signal. */ |
| pt = &active_timers[ts->clock->type]; |
| for(;;) { |
| t = *pt; |
| if (!t) |
| break; |
| if (t->expire_time > expire_time) |
| break; |
| pt = &t->next; |
| } |
| ts->expire_time = expire_time; |
| ts->next = *pt; |
| *pt = ts; |
| |
| /* Rearm if necessary */ |
| if (pt == &active_timers[ts->clock->type]) { |
| if ((alarm_timer->flags & ALARM_FLAG_EXPIRED) == 0) { |
| qemu_rearm_alarm_timer(alarm_timer); |
| } |
| /* Interrupt execution to force deadline recalculation. */ |
| if (use_icount && cpu_single_env) { |
| cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); |
| } |
| } |
| } |
| |
| int qemu_timer_pending(QEMUTimer *ts) |
| { |
| QEMUTimer *t; |
| for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { |
| if (t == ts) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static inline int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) |
| { |
| if (!timer_head) |
| return 0; |
| return (timer_head->expire_time <= current_time); |
| } |
| |
| static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) |
| { |
| QEMUTimer *ts; |
| |
| for(;;) { |
| ts = *ptimer_head; |
| if (!ts || ts->expire_time > current_time) |
| break; |
| /* remove timer from the list before calling the callback */ |
| *ptimer_head = ts->next; |
| ts->next = NULL; |
| |
| /* run the callback (the timer list can be modified) */ |
| ts->cb(ts->opaque); |
| } |
| } |
| |
| int64_t qemu_get_clock(QEMUClock *clock) |
| { |
| switch(clock->type) { |
| case QEMU_TIMER_REALTIME: |
| return get_clock() / 1000000; |
| default: |
| case QEMU_TIMER_VIRTUAL: |
| if (use_icount) { |
| return cpu_get_icount(); |
| } else { |
| return cpu_get_clock(); |
| } |
| } |
| } |
| |
| static void init_timers(void) |
| { |
| init_get_clock(); |
| ticks_per_sec = QEMU_TIMER_BASE; |
| rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME); |
| vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL); |
| } |
| |
| /* save a timer */ |
| void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) |
| { |
| uint64_t expire_time; |
| |
| if (qemu_timer_pending(ts)) { |
| expire_time = ts->expire_time; |
| } else { |
| expire_time = -1; |
| } |
| qemu_put_be64(f, expire_time); |
| } |
| |
| void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) |
| { |
| uint64_t expire_time; |
| |
| expire_time = qemu_get_be64(f); |
| if (expire_time != -1) { |
| qemu_mod_timer(ts, expire_time); |
| } else { |
| qemu_del_timer(ts); |
| } |
| } |
| |
| static void timer_save(QEMUFile *f, void *opaque) |
| { |
| if (cpu_ticks_enabled) { |
| hw_error("cannot save state if virtual timers are running"); |
| } |
| qemu_put_be64(f, cpu_ticks_offset); |
| qemu_put_be64(f, ticks_per_sec); |
| qemu_put_be64(f, cpu_clock_offset); |
| } |
| |
| static int timer_load(QEMUFile *f, void *opaque, int version_id) |
| { |
| if (version_id != 1 && version_id != 2) |
| return -EINVAL; |
| if (cpu_ticks_enabled) { |
| return -EINVAL; |
| } |
| cpu_ticks_offset=qemu_get_be64(f); |
| ticks_per_sec=qemu_get_be64(f); |
| if (version_id == 2) { |
| cpu_clock_offset=qemu_get_be64(f); |
| } |
| return 0; |
| } |
| |
| #ifdef _WIN32 |
| void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, |
| DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) |
| #else |
| static void host_alarm_handler(int host_signum) |
| #endif |
| { |
| #if 0 |
| #define DISP_FREQ 1000 |
| { |
| static int64_t delta_min = INT64_MAX; |
| static int64_t delta_max, delta_cum, last_clock, delta, ti; |
| static int count; |
| ti = qemu_get_clock(vm_clock); |
| if (last_clock != 0) { |
| delta = ti - last_clock; |
| if (delta < delta_min) |
| delta_min = delta; |
| if (delta > delta_max) |
| delta_max = delta; |
| delta_cum += delta; |
| if (++count == DISP_FREQ) { |
| printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n", |
| muldiv64(delta_min, 1000000, ticks_per_sec), |
| muldiv64(delta_max, 1000000, ticks_per_sec), |
| muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec), |
| (double)ticks_per_sec / ((double)delta_cum / DISP_FREQ)); |
| count = 0; |
| delta_min = INT64_MAX; |
| delta_max = 0; |
| delta_cum = 0; |
| } |
| } |
| last_clock = ti; |
| } |
| #endif |
| if (alarm_has_dynticks(alarm_timer) || |
| (!use_icount && |
| qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], |
| qemu_get_clock(vm_clock))) || |
| qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], |
| qemu_get_clock(rt_clock))) { |
| #ifdef _WIN32 |
| struct qemu_alarm_win32 *data = ((struct qemu_alarm_timer*)dwUser)->priv; |
| SetEvent(data->host_alarm); |
| #endif |
| CPUState *env = next_cpu; |
| |
| alarm_timer->flags |= ALARM_FLAG_EXPIRED; |
| |
| if (env) { |
| /* stop the currently executing cpu because a timer occured */ |
| cpu_interrupt(env, CPU_INTERRUPT_EXIT); |
| #ifdef USE_KQEMU |
| if (env->kqemu_enabled) { |
| kqemu_cpu_interrupt(env); |
| } |
| #endif |
| } |
| event_pending = 1; |
| } |
| } |
| |
| static int64_t qemu_next_deadline(void) |
| { |
| int64_t delta; |
| |
| if (active_timers[QEMU_TIMER_VIRTUAL]) { |
| delta = active_timers[QEMU_TIMER_VIRTUAL]->expire_time - |
| qemu_get_clock(vm_clock); |
| } else { |
| /* To avoid problems with overflow limit this to 2^32. */ |
| delta = INT32_MAX; |
| } |
| |
| if (delta < 0) |
| delta = 0; |
| |
| return delta; |
| } |
| |
| #if defined(__linux__) || defined(_WIN32) |
| static uint64_t qemu_next_deadline_dyntick(void) |
| { |
| int64_t delta; |
| int64_t rtdelta; |
| |
| if (use_icount) |
| delta = INT32_MAX; |
| else |
| delta = (qemu_next_deadline() + 999) / 1000; |
| |
| if (active_timers[QEMU_TIMER_REALTIME]) { |
| rtdelta = (active_timers[QEMU_TIMER_REALTIME]->expire_time - |
| qemu_get_clock(rt_clock))*1000; |
| if (rtdelta < delta) |
| delta = rtdelta; |
| } |
| |
| if (delta < MIN_TIMER_REARM_US) |
| delta = MIN_TIMER_REARM_US; |
| |
| return delta; |
| } |
| #endif |
| |
| #ifndef _WIN32 |
| |
| #if defined(__linux__) |
| |
| #define RTC_FREQ 1024 |
| |
| static void enable_sigio_timer(int fd) |
| { |
| struct sigaction act; |
| |
| /* timer signal */ |
| sigfillset(&act.sa_mask); |
| act.sa_flags = 0; |
| act.sa_handler = host_alarm_handler; |
| |
| sigaction(SIGIO, &act, NULL); |
| fcntl(fd, F_SETFL, O_ASYNC); |
| fcntl(fd, F_SETOWN, getpid()); |
| } |
| |
| static int hpet_start_timer(struct qemu_alarm_timer *t) |
| { |
| struct hpet_info info; |
| int r, fd; |
| |
| fd = open("/dev/hpet", O_RDONLY); |
| if (fd < 0) |
| return -1; |
| |
| /* Set frequency */ |
| r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ); |
| if (r < 0) { |
| fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n" |
| "error, but for better emulation accuracy type:\n" |
| "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n"); |
| goto fail; |
| } |
| |
| /* Check capabilities */ |
| r = ioctl(fd, HPET_INFO, &info); |
| if (r < 0) |
| goto fail; |
| |
| /* Enable periodic mode */ |
| r = ioctl(fd, HPET_EPI, 0); |
| if (info.hi_flags && (r < 0)) |
| goto fail; |
| |
| /* Enable interrupt */ |
| r = ioctl(fd, HPET_IE_ON, 0); |
| if (r < 0) |
| goto fail; |
| |
| enable_sigio_timer(fd); |
| t->priv = (void *)(long)fd; |
| |
| return 0; |
| fail: |
| close(fd); |
| return -1; |
| } |
| |
| static void hpet_stop_timer(struct qemu_alarm_timer *t) |
| { |
| int fd = (long)t->priv; |
| |
| close(fd); |
| } |
| |
| static int rtc_start_timer(struct qemu_alarm_timer *t) |
| { |
| int rtc_fd; |
| unsigned long current_rtc_freq = 0; |
| |
| TFR(rtc_fd = open("/dev/rtc", O_RDONLY)); |
| if (rtc_fd < 0) |
| return -1; |
| ioctl(rtc_fd, RTC_IRQP_READ, ¤t_rtc_freq); |
| if (current_rtc_freq != RTC_FREQ && |
| ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { |
| fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" |
| "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" |
| "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); |
| goto fail; |
| } |
| if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { |
| fail: |
| close(rtc_fd); |
| return -1; |
| } |
| |
| enable_sigio_timer(rtc_fd); |
| |
| t->priv = (void *)(long)rtc_fd; |
| |
| return 0; |
| } |
| |
| static void rtc_stop_timer(struct qemu_alarm_timer *t) |
| { |
| int rtc_fd = (long)t->priv; |
| |
| close(rtc_fd); |
| } |
| |
| static int dynticks_start_timer(struct qemu_alarm_timer *t) |
| { |
| struct sigevent ev; |
| timer_t host_timer; |
| struct sigaction act; |
| |
| sigfillset(&act.sa_mask); |
| act.sa_flags = 0; |
| act.sa_handler = host_alarm_handler; |
| |
| sigaction(SIGALRM, &act, NULL); |
| |
| ev.sigev_value.sival_int = 0; |
| ev.sigev_notify = SIGEV_SIGNAL; |
| ev.sigev_signo = SIGALRM; |
| |
| if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { |
| perror("timer_create"); |
| |
| /* disable dynticks */ |
| fprintf(stderr, "Dynamic Ticks disabled\n"); |
| |
| return -1; |
| } |
| |
| t->priv = (void *)host_timer; |
| |
| return 0; |
| } |
| |
| static void dynticks_stop_timer(struct qemu_alarm_timer *t) |
| { |
| timer_t host_timer = (timer_t)t->priv; |
| |
| timer_delete(host_timer); |
| } |
| |
| static void dynticks_rearm_timer(struct qemu_alarm_timer *t) |
| { |
| timer_t host_timer = (timer_t)t->priv; |
| struct itimerspec timeout; |
| int64_t nearest_delta_us = INT64_MAX; |
| int64_t current_us; |
| |
| if (!active_timers[QEMU_TIMER_REALTIME] && |
| !active_timers[QEMU_TIMER_VIRTUAL]) |
| return; |
| |
| nearest_delta_us = qemu_next_deadline_dyntick(); |
| |
| /* check whether a timer is already running */ |
| if (timer_gettime(host_timer, &timeout)) { |
| perror("gettime"); |
| fprintf(stderr, "Internal timer error: aborting\n"); |
| exit(1); |
| } |
| current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; |
| if (current_us && current_us <= nearest_delta_us) |
| return; |
| |
| timeout.it_interval.tv_sec = 0; |
| timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ |
| timeout.it_value.tv_sec = nearest_delta_us / 1000000; |
| timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; |
| if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { |
| perror("settime"); |
| fprintf(stderr, "Internal timer error: aborting\n"); |
| exit(1); |
| } |
| } |
| |
| #endif /* defined(__linux__) */ |
| |
| static int unix_start_timer(struct qemu_alarm_timer *t) |
| { |
| struct sigaction act; |
| struct itimerval itv; |
| int err; |
| |
| /* timer signal */ |
| sigfillset(&act.sa_mask); |
| act.sa_flags = 0; |
| act.sa_handler = host_alarm_handler; |
| |
| sigaction(SIGALRM, &act, NULL); |
| |
| itv.it_interval.tv_sec = 0; |
| /* for i386 kernel 2.6 to get 1 ms */ |
| itv.it_interval.tv_usec = 999; |
| itv.it_value.tv_sec = 0; |
| itv.it_value.tv_usec = 10 * 1000; |
| |
| err = setitimer(ITIMER_REAL, &itv, NULL); |
| if (err) |
| return -1; |
| |
| return 0; |
| } |
| |
| static void unix_stop_timer(struct qemu_alarm_timer *t) |
| { |
| struct itimerval itv; |
| |
| memset(&itv, 0, sizeof(itv)); |
| setitimer(ITIMER_REAL, &itv, NULL); |
| } |
| |
| #endif /* !defined(_WIN32) */ |
| |
| #ifdef _WIN32 |
| |
| static int win32_start_timer(struct qemu_alarm_timer *t) |
| { |
| TIMECAPS tc; |
| struct qemu_alarm_win32 *data = t->priv; |
| UINT flags; |
| |
| data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); |
| if (!data->host_alarm) { |
| perror("Failed CreateEvent"); |
| return -1; |
| } |
| |
| memset(&tc, 0, sizeof(tc)); |
| timeGetDevCaps(&tc, sizeof(tc)); |
| |
| if (data->period < tc.wPeriodMin) |
| data->period = tc.wPeriodMin; |
| |
| timeBeginPeriod(data->period); |
| |
| flags = TIME_CALLBACK_FUNCTION; |
| if (alarm_has_dynticks(t)) |
| flags |= TIME_ONESHOT; |
| else |
| flags |= TIME_PERIODIC; |
| |
| data->timerId = timeSetEvent(1, // interval (ms) |
| data->period, // resolution |
| host_alarm_handler, // function |
| (DWORD)t, // parameter |
| flags); |
| |
| if (!data->timerId) { |
| perror("Failed to initialize win32 alarm timer"); |
| |
| timeEndPeriod(data->period); |
| CloseHandle(data->host_alarm); |
| return -1; |
| } |
| |
| qemu_add_wait_object(data->host_alarm, NULL, NULL); |
| |
| return 0; |
| } |
| |
| static void win32_stop_timer(struct qemu_alarm_timer *t) |
| { |
| struct qemu_alarm_win32 *data = t->priv; |
| |
| timeKillEvent(data->timerId); |
| timeEndPeriod(data->period); |
| |
| CloseHandle(data->host_alarm); |
| } |
| |
| static void win32_rearm_timer(struct qemu_alarm_timer *t) |
| { |
| struct qemu_alarm_win32 *data = t->priv; |
| uint64_t nearest_delta_us; |
| |
| if (!active_timers[QEMU_TIMER_REALTIME] && |
| !active_timers[QEMU_TIMER_VIRTUAL]) |
| return; |
| |
| nearest_delta_us = qemu_next_deadline_dyntick(); |
| nearest_delta_us /= 1000; |
| |
| timeKillEvent(data->timerId); |
| |
| data->timerId = timeSetEvent(1, |
| data->period, |
| host_alarm_handler, |
| (DWORD)t, |
| TIME_ONESHOT | TIME_PERIODIC); |
| |
| if (!data->timerId) { |
| perror("Failed to re-arm win32 alarm timer"); |
| |
| timeEndPeriod(data->period); |
| CloseHandle(data->host_alarm); |
| exit(1); |
| } |
| } |
| |
| #endif /* _WIN32 */ |
| |
| static void init_timer_alarm(void) |
| { |
| struct qemu_alarm_timer *t; |
| int i, err = -1; |
| |
| for (i = 0; alarm_timers[i].name; i++) { |
| t = &alarm_timers[i]; |
| |
| err = t->start(t); |
| if (!err) |
| break; |
| } |
| |
| if (err) { |
| fprintf(stderr, "Unable to find any suitable alarm timer.\n"); |
| fprintf(stderr, "Terminating\n"); |
| exit(1); |
| } |
| |
| alarm_timer = t; |
| } |
| |
| static void quit_timers(void) |
| { |
| alarm_timer->stop(alarm_timer); |
| alarm_timer = NULL; |
| } |
| |
| /***********************************************************/ |
| /* host time/date access */ |
| void qemu_get_timedate(struct tm *tm, int offset) |
| { |
| time_t ti; |
| struct tm *ret; |
| |
| time(&ti); |
| ti += offset; |
| if (rtc_date_offset == -1) { |
| if (rtc_utc) |
| ret = gmtime(&ti); |
| else |
| ret = localtime(&ti); |
| } else { |
| ti -= rtc_date_offset; |
| ret = gmtime(&ti); |
| } |
| |
| memcpy(tm, ret, sizeof(struct tm)); |
| } |
| |
| int qemu_timedate_diff(struct tm *tm) |
| { |
| time_t seconds; |
| |
| if (rtc_date_offset == -1) |
| if (rtc_utc) |
| seconds = mktimegm(tm); |
| else |
| seconds = mktime(tm); |
| else |
| seconds = mktimegm(tm) + rtc_date_offset; |
| |
| return seconds - time(NULL); |
| } |
| |
| |
| #ifdef CONFIG_TRACE |
| static int tbflush_requested; |
| static int exit_requested; |
| |
| void start_tracing() |
| { |
| if (trace_filename == NULL) |
| return; |
| if (!tracing) { |
| fprintf(stderr,"-- start tracing --\n"); |
| start_time = Now(); |
| } |
| tracing = 1; |
| tbflush_requested = 1; |
| if (cpu_single_env) |
| cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); |
| } |
| |
| void stop_tracing() |
| { |
| if (trace_filename == NULL) |
| return; |
| if (tracing) { |
| end_time = Now(); |
| elapsed_usecs += end_time - start_time; |
| fprintf(stderr,"-- stop tracing --\n"); |
| } |
| tracing = 0; |
| tbflush_requested = 1; |
| if (cpu_single_env) |
| cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); |
| } |
| |
| #ifndef _WIN32 |
| /* This is the handler for the SIGUSR1 and SIGUSR2 signals. |
| * SIGUSR1 turns tracing on. SIGUSR2 turns tracing off. |
| */ |
| void sigusr_handler(int sig) |
| { |
| if (sig == SIGUSR1) |
| start_tracing(); |
| else |
| stop_tracing(); |
| } |
| #endif |
| |
| /* This is the handler to catch control-C so that we can exit cleanly. |
| * This is needed when tracing to flush the buffers to disk. |
| */ |
| void sigint_handler(int sig) |
| { |
| exit_requested = 1; |
| if (cpu_single_env) |
| cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); |
| } |
| #endif /* CONFIG_TRACE */ |
| |
| |
| /***********************************************************/ |
| /* character device */ |
| |
| static void qemu_chr_event(CharDriverState *s, int event) |
| { |
| if (!s->chr_event) |
| return; |
| s->chr_event(s->handler_opaque, event); |
| } |
| |
| static void qemu_chr_reset_bh(void *opaque) |
| { |
| CharDriverState *s = opaque; |
| qemu_chr_event(s, CHR_EVENT_RESET); |
| qemu_bh_delete(s->bh); |
| s->bh = NULL; |
| } |
| |
| void qemu_chr_reset(CharDriverState *s) |
| { |
| if (s->bh == NULL) { |
| s->bh = qemu_bh_new(qemu_chr_reset_bh, s); |
| qemu_bh_schedule(s->bh); |
| } |
| } |
| |
| int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) |
| { |
| return s->chr_write(s, buf, len); |
| } |
| |
| int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) |
| { |
| if (!s->chr_ioctl) |
| return -ENOTSUP; |
| return s->chr_ioctl(s, cmd, arg); |
| } |
| |
| int qemu_chr_can_read(CharDriverState *s) |
| { |
| if (!s->chr_can_read) |
| return 0; |
| return s->chr_can_read(s->handler_opaque); |
| } |
| |
| void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len) |
| { |
| s->chr_read(s->handler_opaque, buf, len); |
| } |
| |
| void qemu_chr_accept_input(CharDriverState *s) |
| { |
| if (s->chr_accept_input) |
| s->chr_accept_input(s); |
| } |
| |
| void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) |
| { |
| char buf[4096]; |
| va_list ap; |
| va_start(ap, fmt); |
| vsnprintf(buf, sizeof(buf), fmt, ap); |
| qemu_chr_write(s, (uint8_t *)buf, strlen(buf)); |
| va_end(ap); |
| } |
| |
| void qemu_chr_send_event(CharDriverState *s, int event) |
| { |
| if (s->chr_send_event) |
| s->chr_send_event(s, event); |
| } |
| |
| void qemu_chr_add_handlers(CharDriverState *s, |
| IOCanRWHandler *fd_can_read, |
| IOReadHandler *fd_read, |
| IOEventHandler *fd_event, |
| void *opaque) |
| { |
| s->chr_can_read = fd_can_read; |
| s->chr_read = fd_read; |
| s->chr_event = fd_event; |
| s->handler_opaque = opaque; |
| if (s->chr_update_read_handler) |
| s->chr_update_read_handler(s); |
| } |
| |
| static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) |
| { |
| return len; |
| } |
| |
| static CharDriverState *qemu_chr_open_null(void) |
| { |
| CharDriverState *chr; |
| |
| chr = qemu_mallocz(sizeof(CharDriverState)); |
| if (!chr) |
| return NULL; |
| chr->chr_write = null_chr_write; |
| return chr; |
| } |
| |
| /* MUX driver for serial I/O splitting */ |
| static int term_timestamps; |
| static int64_t term_timestamps_start; |
| #define MAX_MUX 4 |
| #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ |
| #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) |
| typedef struct { |
| IOCanRWHandler *chr_can_read[MAX_MUX]; |
| IOReadHandler *chr_read[MAX_MUX]; |
| IOEventHandler *chr_event[MAX_MUX]; |
| void *ext_opaque[MAX_MUX]; |
| CharDriverState *drv; |
| unsigned char buffer[MUX_BUFFER_SIZE]; |
| int prod; |
| int cons; |
| int mux_cnt; |
| int term_got_escape; |
| int max_size; |
| } MuxDriver; |
| |
| |
| static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) |
| { |
| MuxDriver *d = chr->opaque; |
| int ret; |
| if (!term_timestamps) { |
| ret = d->drv->chr_write(d->drv, buf, len); |
| } else { |
| int i; |
| |
| ret = 0; |
| for(i = 0; i < len; i++) { |
| ret += d->drv->chr_write(d->drv, buf+i, 1); |
| if (buf[i] == '\n') { |
| char buf1[64]; |
| int64_t ti; |
| int secs; |
| |
| ti = get_clock(); |
| if (term_timestamps_start == -1) |
| term_timestamps_start = ti; |
| ti -= term_timestamps_start; |
| secs = ti / 1000000000; |
| snprintf(buf1, sizeof(buf1), |
| "[%02d:%02d:%02d.%03d] ", |
| secs / 3600, |
| (secs / 60) % 60, |
| secs % 60, |
| (int)((ti / 1000000) % 1000)); |
| d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1)); |
| } |
| } |
| } |
| return ret; |
| } |
| |
| static const char * const mux_help[] = { |
| "% h print this help\n\r", |
| "% x exit emulator\n\r", |
| "% s save disk data back to file (if -snapshot)\n\r", |
| "% t toggle console timestamps\n\r" |
| "% b send break (magic sysrq)\n\r", |
| "% c switch between console and monitor\n\r", |
| "% % sends %\n\r", |
| NULL |
| }; |
| |
| static int term_escape_char = 0x01; /* ctrl-a is used for escape */ |
| static void mux_print_help(CharDriverState *chr) |
| { |
| int i, j; |
| char ebuf[15] = "Escape-Char"; |
| char cbuf[50] = "\n\r"; |
| |
| if (term_escape_char > 0 && term_escape_char < 26) { |
| snprintf(cbuf, sizeof(cbuf), "\n\r"); |
| snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a'); |
| } else { |
| snprintf(cbuf, sizeof(cbuf), |
| "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", |
| term_escape_char); |
| } |
| chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf)); |
| for (i = 0; mux_help[i] != NULL; i++) { |
| for (j=0; mux_help[i][j] != '\0'; j++) { |
| if (mux_help[i][j] == '%') |
| chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf)); |
| else |
| chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1); |
| } |
| } |
| } |
| |
| static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) |
| { |
| if (d->term_got_escape) { |
| d->term_got_escape = 0; |
| if (ch == term_escape_char) |
| goto send_char; |
| switch(ch) { |
| case '?': |
| case 'h': |
| mux_print_help(chr); |
| break; |
| case 'x': |
| { |
| const char *term = "QEMU: Terminated\n\r"; |
| chr->chr_write(chr,(uint8_t *)term,strlen(term)); |
| exit(0); |
| break; |
| } |
| case 's': |
| { |
| int i; |
| for (i = 0; i < nb_drives; i++) { |
| bdrv_commit(drives_table[i].bdrv); |
| } |
| } |
| break; |
| case 'b': |
| qemu_chr_event(chr, CHR_EVENT_BREAK); |
| break; |
| case 'c': |
| /* Switch to the next registered device */ |
| chr->focus++; |
| if (chr->focus >= d->mux_cnt) |
| chr->focus = 0; |
| break; |
| case 't': |
| term_timestamps = !term_timestamps; |
| term_timestamps_start = -1; |
| break; |
| } |
| } else if (ch == term_escape_char) { |
| d->term_got_escape = 1; |
| } else { |
| send_char: |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void mux_chr_accept_input(CharDriverState *chr) |
| { |
| int m = chr->focus; |
| MuxDriver *d = chr->opaque; |
| |
| while (d->prod != d->cons && |
| d->chr_can_read[m] && |
| d->chr_can_read[m](d->ext_opaque[m])) { |
| d->chr_read[m](d->ext_opaque[m], |
| &d->buffer[d->cons++ & MUX_BUFFER_MASK], 1); |
| } |
| } |
| |
| static int mux_chr_can_read(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| MuxDriver *d = chr->opaque; |
| |
| if ((d->prod - d->cons) < MUX_BUFFER_SIZE) |
| return 1; |
| if (d->chr_can_read[chr->focus]) |
| return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]); |
| return 0; |
| } |
| |
| static void mux_chr_read(void *opaque, const uint8_t *buf, int size) |
| { |
| CharDriverState *chr = opaque; |
| MuxDriver *d = chr->opaque; |
| int m = chr->focus; |
| int i; |
| |
| mux_chr_accept_input (opaque); |
| |
| for(i = 0; i < size; i++) |
| if (mux_proc_byte(chr, d, buf[i])) { |
| if (d->prod == d->cons && |
| d->chr_can_read[m] && |
| d->chr_can_read[m](d->ext_opaque[m])) |
| d->chr_read[m](d->ext_opaque[m], &buf[i], 1); |
| else |
| d->buffer[d->prod++ & MUX_BUFFER_MASK] = buf[i]; |
| } |
| } |
| |
| static void mux_chr_event(void *opaque, int event) |
| { |
| CharDriverState *chr = opaque; |
| MuxDriver *d = chr->opaque; |
| int i; |
| |
| /* Send the event to all registered listeners */ |
| for (i = 0; i < d->mux_cnt; i++) |
| if (d->chr_event[i]) |
| d->chr_event[i](d->ext_opaque[i], event); |
| } |
| |
| static void mux_chr_update_read_handler(CharDriverState *chr) |
| { |
| MuxDriver *d = chr->opaque; |
| |
| if (d->mux_cnt >= MAX_MUX) { |
| fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); |
| return; |
| } |
| d->ext_opaque[d->mux_cnt] = chr->handler_opaque; |
| d->chr_can_read[d->mux_cnt] = chr->chr_can_read; |
| d->chr_read[d->mux_cnt] = chr->chr_read; |
| d->chr_event[d->mux_cnt] = chr->chr_event; |
| /* Fix up the real driver with mux routines */ |
| if (d->mux_cnt == 0) { |
| qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, |
| mux_chr_event, chr); |
| } |
| chr->focus = d->mux_cnt; |
| d->mux_cnt++; |
| } |
| |
| static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) |
| { |
| CharDriverState *chr; |
| MuxDriver *d; |
| |
| chr = qemu_mallocz(sizeof(CharDriverState)); |
| if (!chr) |
| return NULL; |
| d = qemu_mallocz(sizeof(MuxDriver)); |
| if (!d) { |
| free(chr); |
| return NULL; |
| } |
| |
| chr->opaque = d; |
| d->drv = drv; |
| chr->focus = -1; |
| chr->chr_write = mux_chr_write; |
| chr->chr_update_read_handler = mux_chr_update_read_handler; |
| chr->chr_accept_input = mux_chr_accept_input; |
| return chr; |
| } |
| |
| |
| #ifdef _WIN32 |
| |
| static int send_all(int fd, const uint8_t *buf, int len1) |
| { |
| int ret, len; |
| |
| len = len1; |
| while (len > 0) { |
| ret = socket_send(fd, buf, len); |
| if (ret < 0) { |
| if (errno != EWOULDBLOCK) { |
| return -1; |
| } |
| } else if (ret == 0) { |
| break; |
| } else { |
| buf += ret; |
| len -= ret; |
| } |
| } |
| return len1 - len; |
| } |
| |
| #else |
| |
| static int unix_write(int fd, const uint8_t *buf, int len1) |
| { |
| int ret, len; |
| |
| len = len1; |
| while (len > 0) { |
| ret = write(fd, buf, len); |
| if (ret < 0) { |
| if (errno != EINTR && errno != EAGAIN) |
| return -1; |
| } else if (ret == 0) { |
| break; |
| } else { |
| buf += ret; |
| len -= ret; |
| } |
| } |
| return len1 - len; |
| } |
| |
| static inline int send_all(int fd, const uint8_t *buf, int len1) |
| { |
| return unix_write(fd, buf, len1); |
| } |
| #endif /* !_WIN32 */ |
| |
| #ifndef _WIN32 |
| |
| typedef struct { |
| int fd_in, fd_out; |
| int max_size; |
| } FDCharDriver; |
| |
| #define STDIO_MAX_CLIENTS 1 |
| static int stdio_nb_clients = 0; |
| |
| static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) |
| { |
| FDCharDriver *s = chr->opaque; |
| return unix_write(s->fd_out, buf, len); |
| } |
| |
| static int fd_chr_read_poll(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| FDCharDriver *s = chr->opaque; |
| |
| s->max_size = qemu_chr_can_read(chr); |
| return s->max_size; |
| } |
| |
| static void fd_chr_read(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| FDCharDriver *s = chr->opaque; |
| int size, len; |
| uint8_t buf[1024]; |
| |
| len = sizeof(buf); |
| if (len > s->max_size) |
| len = s->max_size; |
| if (len == 0) |
| return; |
| size = read(s->fd_in, buf, len); |
| if (size == 0) { |
| /* FD has been closed. Remove it from the active list. */ |
| qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); |
| return; |
| } |
| if (size > 0) { |
| qemu_chr_read(chr, buf, size); |
| } |
| } |
| |
| static void fd_chr_update_read_handler(CharDriverState *chr) |
| { |
| FDCharDriver *s = chr->opaque; |
| |
| if (s->fd_in >= 0) { |
| if (nographic && s->fd_in == 0) { |
| } else { |
| qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, |
| fd_chr_read, NULL, chr); |
| } |
| } |
| } |
| |
| static void fd_chr_close(struct CharDriverState *chr) |
| { |
| FDCharDriver *s = chr->opaque; |
| |
| if (s->fd_in >= 0) { |
| if (nographic && s->fd_in == 0) { |
| } else { |
| qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); |
| } |
| } |
| |
| qemu_free(s); |
| } |
| |
| /* open a character device to a unix fd */ |
| static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) |
| { |
| CharDriverState *chr; |
| FDCharDriver *s; |
| |
| chr = qemu_mallocz(sizeof(CharDriverState)); |
| if (!chr) |
| return NULL; |
| s = qemu_mallocz(sizeof(FDCharDriver)); |
| if (!s) { |
| free(chr); |
| return NULL; |
| } |
| s->fd_in = fd_in; |
| s->fd_out = fd_out; |
| chr->opaque = s; |
| chr->chr_write = fd_chr_write; |
| chr->chr_update_read_handler = fd_chr_update_read_handler; |
| chr->chr_close = fd_chr_close; |
| |
| qemu_chr_reset(chr); |
| |
| return chr; |
| } |
| |
| static CharDriverState *qemu_chr_open_file_out(const char *file_out) |
| { |
| int fd_out; |
| |
| TFR(fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); |
| if (fd_out < 0) |
| return NULL; |
| return qemu_chr_open_fd(-1, fd_out); |
| } |
| |
| static CharDriverState *qemu_chr_open_pipe(const char *filename) |
| { |
| int fd_in, fd_out; |
| char filename_in[256], filename_out[256]; |
| |
| snprintf(filename_in, 256, "%s.in", filename); |
| snprintf(filename_out, 256, "%s.out", filename); |
| TFR(fd_in = open(filename_in, O_RDWR | O_BINARY)); |
| TFR(fd_out = open(filename_out, O_RDWR | O_BINARY)); |
| if (fd_in < 0 || fd_out < 0) { |
| if (fd_in >= 0) |
| close(fd_in); |
| if (fd_out >= 0) |
| close(fd_out); |
| TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY)); |
| if (fd_in < 0) |
| return NULL; |
| } |
| return qemu_chr_open_fd(fd_in, fd_out); |
| } |
| |
| CharDriverState *qemu_chr_open_fdpair(const char *fd_pair) |
| { |
| int fd_in, fd_out; |
| char *endptr; |
| |
| /* fd_pair should contain two decimal fd values, separated by |
| * a colon. */ |
| endptr = NULL; |
| fd_in = strtol(fd_pair, &endptr, 10); |
| if (endptr == NULL || endptr == fd_pair || *endptr != ':') |
| return NULL; |
| endptr++; // skip colon |
| fd_pair = endptr; |
| endptr = NULL; |
| fd_out = strtol(fd_pair, &endptr, 10); |
| if (endptr == NULL || endptr == fd_pair || *endptr != '\0') |
| return NULL; |
| |
| return qemu_chr_open_fd(fd_in, fd_out); |
| } |
| |
| |
| /* for STDIO, we handle the case where several clients use it |
| (nographic mode) */ |
| |
| #define TERM_FIFO_MAX_SIZE 1 |
| |
| static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; |
| static int term_fifo_size; |
| |
| static int stdio_read_poll(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| |
| /* try to flush the queue if needed */ |
| if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) { |
| qemu_chr_read(chr, term_fifo, 1); |
| term_fifo_size = 0; |
| } |
| /* see if we can absorb more chars */ |
| if (term_fifo_size == 0) |
| return 1; |
| else |
| return 0; |
| } |
| |
| static void stdio_read(void *opaque) |
| { |
| int size; |
| uint8_t buf[1]; |
| CharDriverState *chr = opaque; |
| |
| size = read(0, buf, 1); |
| if (size == 0) { |
| /* stdin has been closed. Remove it from the active list. */ |
| qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); |
| return; |
| } |
| if (size > 0) { |
| if (qemu_chr_can_read(chr) > 0) { |
| qemu_chr_read(chr, buf, 1); |
| } else if (term_fifo_size == 0) { |
| term_fifo[term_fifo_size++] = buf[0]; |
| } |
| } |
| } |
| |
| /* init terminal so that we can grab keys */ |
| static struct termios oldtty; |
| static int old_fd0_flags; |
| static int term_atexit_done; |
| |
| static void term_exit(void) |
| { |
| tcsetattr (0, TCSANOW, &oldtty); |
| fcntl(0, F_SETFL, old_fd0_flags); |
| } |
| |
| static void term_init(void) |
| { |
| struct termios tty; |
| |
| tcgetattr (0, &tty); |
| oldtty = tty; |
| old_fd0_flags = fcntl(0, F_GETFL); |
| |
| tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP |
| |INLCR|IGNCR|ICRNL|IXON); |
| tty.c_oflag |= OPOST; |
| tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); |
| /* if graphical mode, we allow Ctrl-C handling */ |
| if (nographic) |
| tty.c_lflag &= ~ISIG; |
| tty.c_cflag &= ~(CSIZE|PARENB); |
| tty.c_cflag |= CS8; |
| tty.c_cc[VMIN] = 1; |
| tty.c_cc[VTIME] = 0; |
| |
| tcsetattr (0, TCSANOW, &tty); |
| |
| if (!term_atexit_done++) |
| atexit(term_exit); |
| |
| fcntl(0, F_SETFL, O_NONBLOCK); |
| } |
| |
| static void qemu_chr_close_stdio(struct CharDriverState *chr) |
| { |
| term_exit(); |
| stdio_nb_clients--; |
| qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); |
| fd_chr_close(chr); |
| } |
| |
| static CharDriverState *qemu_chr_open_stdio(void) |
| { |
| CharDriverState *chr; |
| |
| if (stdio_nb_clients >= STDIO_MAX_CLIENTS) |
| return NULL; |
| chr = qemu_chr_open_fd(0, 1); |
| chr->chr_close = qemu_chr_close_stdio; |
| qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); |
| stdio_nb_clients++; |
| term_init(); |
| |
| return chr; |
| } |
| |
| #ifdef __sun__ |
| /* Once Solaris has openpty(), this is going to be removed. */ |
| int openpty(int *amaster, int *aslave, char *name, |
| struct termios *termp, struct winsize *winp) |
| { |
| const char *slave; |
| int mfd = -1, sfd = -1; |
| |
| *amaster = *aslave = -1; |
| |
| mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); |
| if (mfd < 0) |
| goto err; |
| |
| if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) |
| goto err; |
| |
| if ((slave = ptsname(mfd)) == NULL) |
| goto err; |
| |
| if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) |
| goto err; |
| |
| if (ioctl(sfd, I_PUSH, "ptem") == -1 || |
| (termp != NULL && tcgetattr(sfd, termp) < 0)) |
| goto err; |
| |
| if (amaster) |
| *amaster = mfd; |
| if (aslave) |
| *aslave = sfd; |
| if (winp) |
| ioctl(sfd, TIOCSWINSZ, winp); |
| |
| return 0; |
| |
| err: |
| if (sfd != -1) |
| close(sfd); |
| close(mfd); |
| return -1; |
| } |
| |
| void cfmakeraw (struct termios *termios_p) |
| { |
| termios_p->c_iflag &= |
| ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); |
| termios_p->c_oflag &= ~OPOST; |
| termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); |
| termios_p->c_cflag &= ~(CSIZE|PARENB); |
| termios_p->c_cflag |= CS8; |
| |
| termios_p->c_cc[VMIN] = 0; |
| termios_p->c_cc[VTIME] = 0; |
| } |
| #endif /* __sun__ */ |
| |
| #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ |
| || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) |
| |
| typedef struct { |
| int fd; |
| int connected; |
| int polling; |
| int read_bytes; |
| QEMUTimer *timer; |
| } PtyCharDriver; |
| |
| static void pty_chr_update_read_handler(CharDriverState *chr); |
| static void pty_chr_state(CharDriverState *chr, int connected); |
| |
| static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) |
| { |
| PtyCharDriver *s = chr->opaque; |
| |
| if (!s->connected) { |
| /* guest sends data, check for (re-)connect */ |
| pty_chr_update_read_handler(chr); |
| return 0; |
| } |
| return unix_write(s->fd, buf, len); |
| } |
| |
| static int pty_chr_read_poll(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| PtyCharDriver *s = chr->opaque; |
| |
| s->read_bytes = qemu_chr_can_read(chr); |
| return s->read_bytes; |
| } |
| |
| static void pty_chr_read(void *opaque) |
| { |
| CharDriverState *chr = opaque; |
| PtyCharDriver *s = chr->opaque; |
| int size, len; |
| uint8_t buf[1024]; |
| |
| len = sizeof(buf); |
| if (len > s->read_bytes) |
| len = s->read_bytes; |
| if (len == 0) |
| return; |
| size = read(s->fd, buf, len); |
| if ((size == -1 && errno == EIO) || |
| (size == 0)) { |
| pty_chr_state(chr, 0); |
| return; |
| } |
| if (size > 0) { |
| pty_chr_state(chr, 1); |
| qemu_chr_read(chr, buf, size); |
| } |
| } |
| |
| static void pty_chr_update_read_handler(CharDriverState *chr) |
| { |
| PtyCharDriver *s = chr->opaque; |
| |
| qemu_set_fd_handler2(s->fd, pty_chr_read_poll, |
| pty_chr_read, NULL, chr); |
| s->polling = 1; |
| /* |
| * Short timeout here: just need wait long enougth that qemu makes |
| * it through the poll loop once. When reconnected we want a |
| * short timeout so we notice it almost instantly. Otherwise |
| * read() gives us -EIO instantly, making pty_chr_state() reset the |
| * timeout to the normal (much longer) poll interval before the |
| * timer triggers. |
| */ |
| qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10); |
| } |
| |
| static void pty_chr_state(CharDriverState *chr, int connected) |
| { |
| PtyCharDriver *s = chr->opaque; |
| |
| if (!connected) { |
| qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); |
| s->connected = 0; |
| s->polling = 0; |
| /* (re-)connect poll interval for idle guests: once per second. |
| * We check more frequently in case the guests sends data to |
| * the virtual device linked to our pty. */ |
| qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); |
| } else { |
| if (!s->connected) |
| qemu_chr_reset(chr); |
| s->connected = 1; |
| } |
| } |
| |
| static void pty_chr_timer(void *opaque) |
| { |
| struct CharDriverState *chr = opaque; |
| PtyCharDriver *s = chr->opaque; |
| |
| if (s->connected) |
| return; |
| if (s->polling) { |
| /* If we arrive here without polling being cleared due |
| * read returning -EIO, then we are (re-)connected */ |
| pty_chr_state(chr, 1); |
| return; |
| } |
| |
| /* Next poll ... */ |
| pty_chr_update_read_handler(chr); |
| } |
| |
| static void pty_chr_close(struct CharDriverState *chr) |
| { |
| PtyCharDriver *s = chr->opaque; |
| |
| qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); |
| close(s->fd); |
| qemu_free(s); |
| } |
| |
| static CharDriverState *qemu_chr_open_pty(void) |
| { |
| CharDriverState *chr; |
| PtyCharDriver *s; |
| struct termios tty; |
| int slave_fd; |
| #if defined(__OpenBSD__) |
| char pty_name[PATH_MAX]; |
| #define q_ptsname(x) pty_name |
| #else |
| char *pty_name = NULL; |
| #define q_ptsname(x) ptsname(x) |
| #endif |
| |
| chr = qemu_mallocz(sizeof(CharDriverState)); |
| if (!chr) |
| return NULL; |
| s = qemu_mallocz(sizeof(PtyCharDriver)); |
| if (!s) { |
| qemu_free(chr); |
| return NULL; |
| } |
| |
| if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { |
| return NULL; |
| } |
| |
| /* Set raw attributes on the pty. */ |
| cfmakeraw(&tty); |
| tcsetattr(slave_fd, TCSAFLUSH, &tty); |
| close(slave_fd); |
| |
| fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd)); |
| |
| chr->opaque = s; |
| chr->chr_write = pty_chr_write; |
| chr->chr_update_read_handler = pty_chr_update_read_handler; |
| chr->chr_close = pty_chr_close; |
| |
| s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr); |
| |
| return chr; |
| } |
| #endif /* __linux__ || __sun__ || __xxxBSD__ */ |
| |
| static void tty_serial_init(int fd, int speed, |
| int parity, int data_bits, int stop_bits) |
| { |
| struct termios tty; |
| speed_t spd; |
| |
| #if 0 |
| printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", |
| speed, parity, data_bits, stop_bits); |
| #endif |
| tcgetattr (fd, &tty); |
| |
| #define MARGIN 1.1 |
| if (speed <= 50 * MARGIN) |
| spd = B50; |
| else if (speed <= 75 * MARGIN) |
| spd = B75; |
| else if (speed <= 300 * MARGIN) |
| spd = B300; |
| else if (speed <= 600 * MARGIN) |
| spd = B600; |
| else if (speed <= 1200 * MARGIN) |
| spd = B1200; |
| else if (speed <= 2400 * MARGIN) |
| spd = B2400; |
| else if (speed <= 4800 * MARGIN) |
| spd = B4800; |
| else if (speed <= 9600 * MARGIN) |
| spd = B9600; |
| else if (speed <= 19200 * MARGIN) |
| spd = B19200; |
| else if (speed <= 38400 * MARGIN) |
| spd = B38400; |
| else if (speed <= 57600 * MARGIN) |
| spd = B57600; |
| else if (speed <= 115200 * MARGIN) |
| spd = B115200; |
| else |
| spd = B115200; |
| |
| cfsetispeed(&tty, spd); |
| cfsetospeed(&tty, spd);
|