/* * QEMU System Emulator * * Copyright (c) 2003-2005 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. */ #include "vl.h" #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #include #ifdef _BSD #include #ifndef __APPLE__ #include #endif #else #include #include #include #include #include #include #endif #endif #if defined(CONFIG_SLIRP) #include "libslirp.h" #endif #ifdef _WIN32 #include #include #include #define getopt_long_only getopt_long #define memalign(align, size) malloc(size) #endif #ifdef CONFIG_SDL #ifdef __APPLE__ #include #endif #endif /* CONFIG_SDL */ #ifdef CONFIG_COCOA #undef main #define main qemu_main #endif /* CONFIG_COCOA */ #include "disas.h" #include "exec-all.h" #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" //#define DEBUG_UNUSED_IOPORT //#define DEBUG_IOPORT #if !defined(CONFIG_SOFTMMU) #define PHYS_RAM_MAX_SIZE (256 * 1024 * 1024) #else #define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024) #endif #ifdef TARGET_PPC #define DEFAULT_RAM_SIZE 144 #else #define DEFAULT_RAM_SIZE 128 #endif /* in ms */ #define GUI_REFRESH_INTERVAL 30 /* XXX: use a two level table to limit memory usage */ #define MAX_IOPORTS 65536 const char *bios_dir = CONFIG_QEMU_SHAREDIR; char phys_ram_file[1024]; void *ioport_opaque[MAX_IOPORTS]; IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD]; int vga_ram_size; int bios_size; static DisplayState display_state; int nographic; const char* keyboard_layout = NULL; int64_t ticks_per_sec; int boot_device = 'c'; int ram_size; int pit_min_timer_count = 0; int nb_nics; NICInfo nd_table[MAX_NICS]; QEMUTimer *gui_timer; int vm_running; #ifdef HAS_AUDIO int audio_enabled = 0; int sb16_enabled = 0; int adlib_enabled = 0; int gus_enabled = 0; int es1370_enabled = 0; #endif int rtc_utc = 1; int cirrus_vga_enabled = 1; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; #else int graphic_width = 800; int graphic_height = 600; #endif int graphic_depth = 15; int full_screen = 0; TextConsole *vga_console; 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; USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; USBDevice *vm_usb_hub; static VLANState *first_vlan; int smp_cpus = 1; #if defined(TARGET_SPARC) #define MAX_CPUS 16 #elif defined(TARGET_I386) #define MAX_CPUS 255 #else #define MAX_CPUS 1 #endif /***********************************************************/ /* x86 ISA bus support */ target_phys_addr_t isa_mem_base = 0; PicState2 *isa_pic; uint32_t default_ioport_readb(void *opaque, uint32_t address) { #ifdef DEBUG_UNUSED_IOPORT fprintf(stderr, "inb: port=0x%04x\n", address); #endif return 0xff; } void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data) { #ifdef DEBUG_UNUSED_IOPORT fprintf(stderr, "outb: port=0x%04x data=0x%02x\n", address, data); #endif } /* default is to make two byte accesses */ uint32_t default_ioport_readw(void *opaque, uint32_t address) { uint32_t data; data = ioport_read_table[0][address](ioport_opaque[address], address); address = (address + 1) & (MAX_IOPORTS - 1); data |= ioport_read_table[0][address](ioport_opaque[address], address) << 8; return data; } void default_ioport_writew(void *opaque, uint32_t address, uint32_t data) { ioport_write_table[0][address](ioport_opaque[address], address, data & 0xff); address = (address + 1) & (MAX_IOPORTS - 1); ioport_write_table[0][address](ioport_opaque[address], address, (data >> 8) & 0xff); } uint32_t default_ioport_readl(void *opaque, uint32_t address) { #ifdef DEBUG_UNUSED_IOPORT fprintf(stderr, "inl: port=0x%04x\n", address); #endif return 0xffffffff; } void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) { #ifdef DEBUG_UNUSED_IOPORT fprintf(stderr, "outl: port=0x%04x data=0x%02x\n", address, data); #endif } void init_ioports(void) { int i; for(i = 0; i < MAX_IOPORTS; i++) { ioport_read_table[0][i] = default_ioport_readb; ioport_write_table[0][i] = default_ioport_writeb; ioport_read_table[1][i] = default_ioport_readw; ioport_write_table[1][i] = default_ioport_writew; ioport_read_table[2][i] = default_ioport_readl; ioport_write_table[2][i] = default_ioport_writel; } } /* 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_read: 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 pstrcpy(char *buf, int buf_size, const char *str) { int c; char *q = buf; if (buf_size <= 0) return; for(;;) { c = *str++; if (c == 0 || q >= buf + buf_size - 1) break; *q++ = c; } *q = '\0'; } /* strcat and truncate. */ char *pstrcat(char *buf, int buf_size, const char *s) { int len; len = strlen(buf); if (len < buf_size) pstrcpy(buf + len, buf_size - len, s); return buf; } int strstart(const char *str, const char *val, const char **ptr) { const char *p, *q; p = str; q = val; while (*q != '\0') { if (*p != *q) return 0; p++; q++; } if (ptr) *ptr = p; return 1; } /* return the size or -1 if error */ int get_image_size(const char *filename) { int fd, size; fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return -1; size = lseek(fd, 0, SEEK_END); close(fd); return size; } /* return the size or -1 if error */ int load_image(const char *filename, uint8_t *addr) { int fd, size; fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return -1; size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); if (read(fd, addr, size) != size) { close(fd); return -1; } close(fd); return size; } 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_table[0][addr](ioport_opaque[addr], addr, val); } 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_table[1][addr](ioport_opaque[addr], addr, val); } 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_table[2][addr](ioport_opaque[addr], addr, val); } int cpu_inb(CPUState *env, int addr) { int val; val = ioport_read_table[0][addr](ioport_opaque[addr], addr); #ifdef DEBUG_IOPORT if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inb : %04x %02x\n", addr, val); #endif return val; } int cpu_inw(CPUState *env, int addr) { int val; val = ioport_read_table[1][addr](ioport_opaque[addr], addr); #ifdef DEBUG_IOPORT if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inw : %04x %04x\n", addr, val); #endif return val; } int cpu_inl(CPUState *env, int addr) { int val; val = ioport_read_table[2][addr](ioport_opaque[addr], addr); #ifdef DEBUG_IOPORT if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inl : %04x %08x\n", addr, val); #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 QEMUPutMouseEvent *qemu_put_mouse_event; static void *qemu_put_mouse_event_opaque; void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { qemu_put_kbd_event_opaque = opaque; qemu_put_kbd_event = func; } void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque) { qemu_put_mouse_event_opaque = opaque; qemu_put_mouse_event = func; } void kbd_put_keycode(int keycode) { if (qemu_put_kbd_event) { qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); } } void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) { if (qemu_put_mouse_event) { qemu_put_mouse_event(qemu_put_mouse_event_opaque, dx, dy, dz, buttons_state); } } /***********************************************************/ /* timers */ #if defined(__powerpc__) static inline uint32_t get_tbl(void) { uint32_t tbl; asm volatile("mftb %0" : "=r" (tbl)); return tbl; } static inline uint32_t get_tbu(void) { uint32_t tbl; asm volatile("mftbu %0" : "=r" (tbl)); return tbl; } int64_t cpu_get_real_ticks(void) { uint32_t l, h, h1; /* NOTE: we test if wrapping has occurred */ do { h = get_tbu(); l = get_tbl(); h1 = get_tbu(); } while (h != h1); return ((int64_t)h << 32) | l; } #elif defined(__i386__) int64_t cpu_get_real_ticks(void) { int64_t val; asm volatile ("rdtsc" : "=A" (val)); return val; } #elif defined(__x86_64__) int64_t cpu_get_real_ticks(void) { uint32_t low,high; int64_t val; asm volatile("rdtsc" : "=a" (low), "=d" (high)); val = high; val <<= 32; val |= low; return val; } #elif defined(__ia64) int64_t cpu_get_real_ticks(void) { int64_t val; asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); return val; } #elif defined(__s390__) int64_t cpu_get_real_ticks(void) { int64_t val; asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); return val; } #else #error unsupported CPU #endif static int64_t cpu_ticks_offset; static int cpu_ticks_enabled; static inline int64_t cpu_get_ticks(void) { if (!cpu_ticks_enabled) { return cpu_ticks_offset; } else { return cpu_get_real_ticks() + cpu_ticks_offset; } } /* enable cpu_get_ticks() */ void cpu_enable_ticks(void) { if (!cpu_ticks_enabled) { cpu_ticks_offset -= cpu_get_real_ticks(); 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_ticks_enabled = 0; } } static int64_t get_clock(void) { #ifdef _WIN32 struct _timeb tb; _ftime(&tb); return ((int64_t)tb.time * 1000 + (int64_t)tb.millitm) * 1000; #else struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000LL + tv.tv_usec; #endif } void cpu_calibrate_ticks(void) { int64_t usec, ticks; usec = get_clock(); ticks = cpu_get_real_ticks(); #ifdef _WIN32 Sleep(50); #else usleep(50 * 1000); #endif usec = get_clock() - usec; ticks = cpu_get_real_ticks() - ticks; ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec; } /* 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; } #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; }; QEMUClock *rt_clock; QEMUClock *vm_clock; static QEMUTimer *active_timers[2]; #ifdef _WIN32 static MMRESULT timerID; #else /* frequency of the times() clock tick */ static int timer_freq; #endif 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; } 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: #ifdef _WIN32 return GetTickCount(); #else { struct tms tp; /* Note that using gettimeofday() is not a good solution for timers because its value change when the date is modified. */ if (timer_freq == 100) { return times(&tp) * 10; } else { return ((int64_t)times(&tp) * 1000) / timer_freq; } } #endif default: case QEMU_TIMER_VIRTUAL: return cpu_get_ticks(); } } /* 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_be64s(f, &cpu_ticks_offset); qemu_put_be64s(f, &ticks_per_sec); } static int timer_load(QEMUFile *f, void *opaque, int version_id) { if (version_id != 1) return -EINVAL; if (cpu_ticks_enabled) { return -EINVAL; } qemu_get_be64s(f, &cpu_ticks_offset); qemu_get_be64s(f, &ticks_per_sec); 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=%lld us max=%lld us avg=%lld 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 (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))) { CPUState *env = cpu_single_env; 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 } } } #ifndef _WIN32 #if defined(__linux__) #define RTC_FREQ 1024 static int rtc_fd; static int start_rtc_timer(void) { rtc_fd = open("/dev/rtc", O_RDONLY); if (rtc_fd < 0) return -1; if (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; } pit_min_timer_count = PIT_FREQ / RTC_FREQ; return 0; } #else static int start_rtc_timer(void) { return -1; } #endif /* !defined(__linux__) */ #endif /* !defined(_WIN32) */ static void init_timers(void) { rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME); vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL); #ifdef _WIN32 { int count=0; timerID = timeSetEvent(1, // interval (ms) 0, // resolution host_alarm_handler, // function (DWORD)&count, // user parameter TIME_PERIODIC | TIME_CALLBACK_FUNCTION); if( !timerID ) { perror("failed timer alarm"); exit(1); } } pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000; #else { struct sigaction act; struct itimerval itv; /* get times() syscall frequency */ timer_freq = sysconf(_SC_CLK_TCK); /* timer signal */ sigfillset(&act.sa_mask); act.sa_flags = 0; #if defined (TARGET_I386) && defined(USE_CODE_COPY) act.sa_flags |= SA_ONSTACK; #endif act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */ itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 10 * 1000; setitimer(ITIMER_REAL, &itv, NULL); /* we probe the tick duration of the kernel to inform the user if the emulated kernel requested a too high timer frequency */ getitimer(ITIMER_REAL, &itv); #if defined(__linux__) if (itv.it_interval.tv_usec > 1000) { /* try to use /dev/rtc to have a faster timer */ if (start_rtc_timer() < 0) goto use_itimer; /* disable itimer */ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); /* use the RTC */ sigaction(SIGIO, &act, NULL); fcntl(rtc_fd, F_SETFL, O_ASYNC); fcntl(rtc_fd, F_SETOWN, getpid()); } else #endif /* defined(__linux__) */ { use_itimer: pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) / 1000000; } } #endif } void quit_timers(void) { #ifdef _WIN32 timeKillEvent(timerID); #endif } /***********************************************************/ /* character device */ 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); } 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, 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_read_handler(CharDriverState *s, IOCanRWHandler *fd_can_read, IOReadHandler *fd_read, void *opaque) { s->chr_add_read_handler(s, fd_can_read, fd_read, opaque); } void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event) { s->chr_event = chr_event; } static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { return len; } static void null_chr_add_read_handler(CharDriverState *chr, IOCanRWHandler *fd_can_read, IOReadHandler *fd_read, void *opaque) { } CharDriverState *qemu_chr_open_null(void) { CharDriverState *chr; chr = qemu_mallocz(sizeof(CharDriverState)); if (!chr) return NULL; chr->chr_write = null_chr_write; chr->chr_add_read_handler = null_chr_add_read_handler; return chr; } #ifndef _WIN32 typedef struct { int fd_in, fd_out; IOCanRWHandler *fd_can_read; IOReadHandler *fd_read; void *fd_opaque; int max_size; } FDCharDriver; #define STDIO_MAX_CLIENTS 2 static int stdio_nb_clients; static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; 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 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 = s->fd_can_read(s->fd_opaque); 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) { s->fd_read(s->fd_opaque, buf, size); } } static void fd_chr_add_read_handler(CharDriverState *chr, IOCanRWHandler *fd_can_read, IOReadHandler *fd_read, void *opaque) { FDCharDriver *s = chr->opaque; if (s->fd_in >= 0) { s->fd_can_read = fd_can_read; s->fd_read = fd_read; s->fd_opaque = opaque; if (nographic && s->fd_in == 0) { } else { qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, fd_chr_read, NULL, chr); } } } /* open a character device to a unix fd */ 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_add_read_handler = fd_chr_add_read_handler; return chr; } CharDriverState *qemu_chr_open_file_out(const char *file_out) { int fd_out; fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY); if (fd_out < 0) return NULL; return qemu_chr_open_fd(-1, fd_out); } CharDriverState *qemu_chr_open_pipe(const char *filename) { int fd; fd = open(filename, O_RDWR | O_BINARY); if (fd < 0) return NULL; return qemu_chr_open_fd(fd, fd); } /* for STDIO, we handle the case where several clients use it (nographic mode) */ #define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ #define TERM_FIFO_MAX_SIZE 1 static int term_got_escape, client_index; static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; int term_fifo_size; void term_print_help(void) { printf("\n" "C-a h print this help\n" "C-a x exit emulator\n" "C-a s save disk data back to file (if -snapshot)\n" "C-a b send break (magic sysrq)\n" "C-a c switch between console and monitor\n" "C-a C-a send C-a\n" ); } /* called when a char is received */ static void stdio_received_byte(int ch) { if (term_got_escape) { term_got_escape = 0; switch(ch) { case 'h': term_print_help(); break; case 'x': exit(0); break; case 's': { int i; for (i = 0; i < MAX_DISKS; i++) { if (bs_table[i]) bdrv_commit(bs_table[i]); } } break; case 'b': if (client_index < stdio_nb_clients) { CharDriverState *chr; FDCharDriver *s; chr = stdio_clients[client_index]; s = chr->opaque; chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK); } break; case 'c': client_index++; if (client_index >= stdio_nb_clients) client_index = 0; if (client_index == 0) { /* send a new line in the monitor to get the prompt */ ch = '\r'; goto send_char; } break; case TERM_ESCAPE: goto send_char; } } else if (ch == TERM_ESCAPE) { term_got_escape = 1; } else { send_char: if (client_index < stdio_nb_clients) { uint8_t buf[1]; CharDriverState *chr; FDCharDriver *s; chr = stdio_clients[client_index]; s = chr->opaque; if (s->fd_can_read(s->fd_opaque) > 0) { buf[0] = ch; s->fd_read(s->fd_opaque, buf, 1); } else if (term_fifo_size == 0) { term_fifo[term_fifo_size++] = ch; } } } } static int stdio_read_poll(void *opaque) { CharDriverState *chr; FDCharDriver *s; if (client_index < stdio_nb_clients) { chr = stdio_clients[client_index]; s = chr->opaque; /* try to flush the queue if needed */ if (term_fifo_size != 0 && s->fd_can_read(s->fd_opaque) > 0) { s->fd_read(s->fd_opaque, term_fifo, 1); term_fifo_size = 0; } /* see if we can absorb more chars */ if (term_fifo_size == 0) return 1; else return 0; } else { return 1; } } static void stdio_read(void *opaque) { int size; uint8_t buf[1]; size = read(0, buf, 1); if (size > 0) stdio_received_byte(buf[0]); } /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; 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); atexit(term_exit); fcntl(0, F_SETFL, O_NONBLOCK); } CharDriverState *qemu_chr_open_stdio(void) { CharDriverState *chr; if (nographic) { if (stdio_nb_clients >= STDIO_MAX_CLIENTS) return NULL; chr = qemu_chr_open_fd(0, 1); if (stdio_nb_clients == 0) qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL); client_index = stdio_nb_clients; } else { if (stdio_nb_clients != 0) return NULL; chr = qemu_chr_open_fd(0, 1); } stdio_clients[stdio_nb_clients++] = chr; if (stdio_nb_clients == 1) { /* set the terminal in raw mode */ term_init(); } return chr; } #if defined(__linux__) CharDriverState *qemu_chr_open_pty(void) { struct termios tty; char slave_name[1024]; int master_fd, slave_fd; /* Not satisfying */ if (openpty(&master_fd, &slave_fd, slave_name, NULL, NULL) < 0) { return NULL; } /* Disabling local echo and line-buffered output */ tcgetattr (master_fd, &tty); tty.c_lflag &= ~(ECHO|ICANON|ISIG); tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; tcsetattr (master_fd, TCSAFLUSH, &tty); fprintf(stderr, "char device redirected to %s\n", slave_name); return qemu_chr_open_fd(master_fd, master_fd); } 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); switch(speed) { case 50: spd = B50; break; case 75: spd = B75; break; case 300: spd = B300; break; case 600: spd = B600; break; case 1200: spd = B1200; break; case 2400: spd = B2400; break; case 4800: spd = B4800; break; case 9600: spd = B9600; break; case 19200: spd = B19200; break; case 38400: spd = B38400; break; case 57600: spd = B57600; break; default: case 115200: spd = B115200; break; } cfsetispeed(&tty, spd); cfsetospeed(&tty, spd); tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP |INLCR|IGNCR|ICRNL|IXON); tty.c_oflag |= OPOST; tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG); tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS); switch(data_bits) { default: case 8: tty.c_cflag |= CS8; break; case 7: tty.c_cflag |= CS7; break; case 6: tty.c_cflag |= CS6; break; case 5: tty.c_cflag |= CS5; break; } switch(parity) { default: case 'N': break; case 'E': tty.c_cflag |= PARENB; break; case 'O': tty.c_cflag |= PARENB | PARODD; break; } tcsetattr (fd, TCSANOW, &tty); } static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { FDCharDriver *s = chr->opaque; switch(cmd) { case CHR_IOCTL_SERIAL_SET_PARAMS: { QEMUSerialSetParams *ssp = arg; tty_serial_init(s->fd_in, ssp->speed, ssp->parity, ssp->data_bits, ssp->stop_bits); } break; case CHR_IOCTL_SERIAL_SET_BREAK: { int enable = *(int *)arg; if (enable) tcsendbreak(s->fd_in, 1); } break; default: return -ENOTSUP; } return 0; } CharDriverState *qemu_chr_open_tty(const char *filename) { CharDriverState *chr; int fd; fd = open(filename, O_RDWR | O_NONBLOCK); if (fd < 0) return NULL; fcntl(fd, F_SETFL, O_NONBLOCK); tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); if (!chr) return NULL; chr->chr_ioctl = tty_serial_ioctl; return chr; } static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { int fd = (int)chr->opaque; uint8_t b; switch(cmd) { case CHR_IOCTL_PP_READ_DATA: if (ioctl(fd, PPRDATA, &b) < 0) return -ENOTSUP; *(uint8_t *)arg = b; break; case CHR_IOCTL_PP_WRITE_DATA: b = *(uint8_t *)arg; if (ioctl(fd, PPWDATA, &b) < 0) return -ENOTSUP; break; case CHR_IOCTL_PP_READ_CONTROL: if (ioctl(fd, PPRCONTROL, &b) < 0) return -ENOTSUP; *(uint8_t *)arg = b; break; case CHR_IOCTL_PP_WRITE_CONTROL: b = *(uint8_t *)arg; if (ioctl(fd, PPWCONTROL, &b) < 0) return -ENOTSUP; break; case CHR_IOCTL_PP_READ_STATUS: if (ioctl(fd, PPRSTATUS, &b) < 0) return -ENOTSUP; *(uint8_t *)arg = b; break; default: return -ENOTSUP; } return 0; } CharDriverState *qemu_chr_open_pp(const char *filename) { CharDriverState *chr; int fd; fd = open(filename, O_RDWR); if (fd < 0) return NULL; if (ioctl(fd, PPCLAIM) < 0) { close(fd); return NULL; } chr = qemu_mallocz(sizeof(CharDriverState)); if (!chr) { close(fd); return NULL; } chr->opaque = (void *)fd; chr->chr_write = null_chr_write; chr->chr_add_read_handler = null_chr_add_read_handler; chr->chr_ioctl = pp_ioctl; return chr; } #else CharDriverState *qemu_chr_open_pty(void) { return NULL; } #endif #endif /* !defined(_WIN32) */ CharDriverState *qemu_chr_open(const char *filename) { const char *p; if (!strcmp(filename, "vc")) { return text_console_init(&display_state); } else if (!strcmp(filename, "null")) { return qemu_chr_open_null(); } else #ifndef _WIN32 if (strstart(filename, "file:", &p)) { return qemu_chr_open_file_out(p); } else if (strstart(filename, "pipe:", &p)) { return qemu_chr_open_pipe(p); } else if (!strcmp(filename, "pty")) { return qemu_chr_open_pty(); } else if (!strcmp(filename, "stdio")) { return qemu_chr_open_stdio(); } else #endif #if defined(__linux__) if (strstart(filename, "/dev/parport", NULL)) { return qemu_chr_open_pp(filename); } else if (strstart(filename, "/dev/", NULL)) { return qemu_chr_open_tty(filename); } else #endif { return NULL; } } /***********************************************************/ /* network device redirectors */ void hex_dump(FILE *f, const uint8_t *buf, int size) { int len, i, j, c; for(i=0;i 16) len = 16; fprintf(f, "%08x ", i); for(j=0;j<16;j++) { if (j < len) fprintf(f, " %02x", buf[i+j]); else fprintf(f, " "); } fprintf(f, " "); for(j=0;j '~') c = '.'; fprintf(f, "%c", c); } fprintf(f, "\n"); } } static int parse_macaddr(uint8_t *macaddr, const char *p) { int i; for(i = 0; i < 6; i++) { macaddr[i] = strtol(p, (char **)&p, 16); if (i == 5) { if (*p != '\0') return -1; } else { if (*p != ':') return -1; p++; } } return 0; } static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { const char *p, *p1; int len; p = *pp; p1 = strchr(p, sep); if (!p1) return -1; len = p1 - p; p1++; if (buf_size > 0) { if (len > buf_size - 1) len = buf_size - 1; memcpy(buf, p, len); buf[len] = '\0'; } *pp = p1; return 0; } int parse_host_port(struct sockaddr_in *saddr, const char *str) { char buf[512]; struct hostent *he; const char *p, *r; int port; p = str; if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) return -1; saddr->sin_family = AF_INET; if (buf[0] == '\0') { saddr->sin_addr.s_addr = 0; } else { if (isdigit(buf[0])) { if (!inet_aton(buf, &saddr->sin_addr)) return -1; } else { #ifdef _WIN32 return -1; #else if ((he = gethostbyname(buf)) == NULL) return - 1; saddr->sin_addr = *(struct in_addr *)he->h_addr; #endif } } port = strtol(p, (char **)&r, 0); if (r == p) return -1; saddr->sin_port = htons(port); return 0; } /* find or alloc a new VLAN */ VLANState *qemu_find_vlan(int id) { VLANState **pvlan, *vlan; for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) { if (vlan->id == id) return vlan; } vlan = qemu_mallocz(sizeof(VLANState)); if (!vlan) return NULL; vlan->id = id; vlan->next = NULL; pvlan = &first_vlan; while (*pvlan != NULL) pvlan = &(*pvlan)->next; *pvlan = vlan; return vlan; } VLANClientState *qemu_new_vlan_client(VLANState *vlan, IOReadHandler *fd_read, void *opaque) { VLANClientState *vc, **pvc; vc = qemu_mallocz(sizeof(VLANClientState)); if (!vc) return NULL; vc->fd_read = fd_read; vc->opaque = opaque; vc->vlan = vlan; vc->next = NULL; pvc = &vlan->first_client; while (*pvc != NULL) pvc = &(*pvc)->next; *pvc = vc; return vc; } void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size) { VLANState *vlan = vc1->vlan; VLANClientState *vc; #if 0 printf("vlan %d send:\n", vlan->id); hex_dump(stdout, buf, size); #endif for(vc = vlan->first_client; vc != NULL; vc = vc->next) { if (vc != vc1) { vc->fd_read(vc->opaque, buf, size); } } } #if defined(CONFIG_SLIRP) /* slirp network adapter */ static int slirp_inited; static VLANClientState *slirp_vc; int slirp_can_output(void) { return 1; } void slirp_output(const uint8_t *pkt, int pkt_len) { #if 0 printf("slirp output:\n"); hex_dump(stdout, pkt, pkt_len); #endif qemu_send_packet(slirp_vc, pkt, pkt_len); } static void slirp_receive(void *opaque, const uint8_t *buf, int size) { #if 0 printf("slirp input:\n"); hex_dump(stdout, buf, size); #endif slirp_input(buf, size); } static int net_slirp_init(VLANState *vlan) { if (!slirp_inited) { slirp_inited = 1; slirp_init(); } slirp_vc = qemu_new_vlan_client(vlan, slirp_receive, NULL); snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector"); return 0; } static void net_slirp_redir(const char *redir_str) { int is_udp; char buf[256], *r; const char *p; struct in_addr guest_addr; int host_port, guest_port; if (!slirp_inited) { slirp_inited = 1; slirp_init(); } p = redir_str; if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) goto fail; if (!strcmp(buf, "tcp")) { is_udp = 0; } else if (!strcmp(buf, "udp")) { is_udp = 1; } else { goto fail; } if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) goto fail; host_port = strtol(buf, &r, 0); if (r == buf) goto fail; if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) goto fail; if (buf[0] == '\0') { pstrcpy(buf, sizeof(buf), "10.0.2.15"); } if (!inet_aton(buf, &guest_addr)) goto fail; guest_port = strtol(p, &r, 0); if (r == p) goto fail; if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) { fprintf(stderr, "qemu: could not set up redirection\n"); exit(1); } return; fail: fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n"); exit(1); } #ifndef _WIN32 char smb_dir[1024]; static void smb_exit(void) { DIR *d; struct dirent *de; char filename[1024]; /* erase all the files in the directory */ d = opendir(smb_dir); for(;;) { de = readdir(d); if (!de) break; if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { snprintf(filename, sizeof(filename), "%s/%s", smb_dir, de->d_name); unlink(filename); } } closedir(d); rmdir(smb_dir); } /* automatic user mode samba server configuration */ void net_slirp_smb(const char *exported_dir) { char smb_conf[1024]; char smb_cmdline[1024]; FILE *f; if (!slirp_inited) { slirp_inited = 1; slirp_init(); } /* XXX: better tmp dir construction */ snprintf(smb_dir, sizeof(smb_dir), "/tmp/qemu-smb.%d", getpid()); if (mkdir(smb_dir, 0700) < 0) { fprintf(stderr, "qemu: could not create samba server dir '%s'\n", smb_dir); exit(1); } snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_dir, "smb.conf"); f = fopen(smb_conf, "w"); if (!f) { fprintf(stderr, "qemu: could not create samba server configuration file '%s'\n", smb_conf); exit(1); } fprintf(f, "[global]\n" "private dir=%s\n" "smb ports=0\n" "socket address=127.0.0.1\n" "pid directory=%s\n" "lock directory=%s\n" "log file=%s/log.smbd\n" "smb passwd file=%s/smbpasswd\n" "security = share\n" "[qemu]\n" "path=%s\n" "read only=no\n" "guest ok=yes\n", smb_dir, smb_dir, smb_dir, smb_dir, smb_dir, exported_dir ); fclose(f); atexit(smb_exit); snprintf(smb_cmdline, sizeof(smb_cmdline), "/usr/sbin/smbd -s %s", smb_conf); slirp_add_exec(0, smb_cmdline, 4, 139); } #endif /* !defined(_WIN32) */ #endif /* CONFIG_SLIRP */ #if !defined(_WIN32) typedef struct TAPState { VLANClientState *vc; int fd; } TAPState; static void tap_receive(void *opaque, const uint8_t *buf, int size) { TAPState *s = opaque; int ret; for(;;) { ret = write(s->fd, buf, size); if (ret < 0 && (errno == EINTR || errno == EAGAIN)) { } else { break; } } } static void tap_send(void *opaque) { TAPState *s = opaque; uint8_t buf[4096]; int size; size = read(s->fd, buf, sizeof(buf)); if (size > 0) { qemu_send_packet(s->vc, buf, size); } } /* fd support */ static TAPState *net_tap_fd_init(VLANState *vlan, int fd) { TAPState *s; s = qemu_mallocz(sizeof(TAPState)); if (!s) return NULL; s->fd = fd; s->vc = qemu_new_vlan_client(vlan, tap_receive, s); qemu_set_fd_handler(s->fd, tap_send, NULL, s); snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd); return s; } #ifdef _BSD static int tap_open(char *ifname, int ifname_size) { int fd; char *dev; struct stat s; fd = open("/dev/tap", O_RDWR); if (fd < 0) { fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n"); return -1; } fstat(fd, &s); dev = devname(s.st_rdev, S_IFCHR); pstrcpy(ifname, ifname_size, dev); fcntl(fd, F_SETFL, O_NONBLOCK); return fd; } #else static int tap_open(char *ifname, int ifname_size) { struct ifreq ifr; int fd, ret; fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n"); return -1; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; if (ifname[0] != '\0') pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); else pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d"); ret = ioctl(fd, TUNSETIFF, (void *) &ifr); if (ret != 0) { fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n"); close(fd); return -1; } pstrcpy(ifname, ifname_size, ifr.ifr_name); fcntl(fd, F_SETFL, O_NONBLOCK); return fd; } #endif static int net_tap_init(VLANState *vlan, const char *ifname1, const char *setup_script) { TAPState *s; int pid, status, fd; char *args[3]; char **parg; char ifname[128]; if (ifname1 != NULL) pstrcpy(ifname, sizeof(ifname), ifname1); else ifname[0] = '\0'; fd = tap_open(ifname, sizeof(ifname)); if (fd < 0) return -1; if (!setup_script) setup_script = ""; if (setup_script[0] != '\0') { /* try to launch network init script */ pid = fork(); if (pid >= 0) { if (pid == 0) { parg = args; *parg++ = (char *)setup_script; *parg++ = ifname; *parg++ = NULL; execv(setup_script, args); _exit(1); } while (waitpid(pid, &status, 0) != pid); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { fprintf(stderr, "%s: could not launch network script\n", setup_script); return -1; } } } s = net_tap_fd_init(vlan, fd); if (!s) return -1; snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: ifname=%s setup_script=%s", ifname, setup_script); return 0; } /* network connection */ typedef struct NetSocketState { VLANClientState *vc; int fd; int state; /* 0 = getting length, 1 = getting data */ int index; int packet_len; uint8_t buf[4096]; struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */ } NetSocketState; typedef struct NetSocketListenState { VLANState *vlan; int fd; } NetSocketListenState; /* XXX: we consider we can send the whole packet without blocking */ static void net_socket_receive(void *opaque, const uint8_t *buf, int size) { NetSocketState *s = opaque; uint32_t len; len = htonl(size); unix_write(s->fd, (const uint8_t *)&len, sizeof(len)); unix_write(s->fd, buf, size); } static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size) { NetSocketState *s = opaque; sendto(s->fd, buf, size, 0, (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst)); } static void net_socket_send(void *opaque) { NetSocketState *s = opaque; int l, size; uint8_t buf1[4096]; const uint8_t *buf; size = read(s->fd, buf1, sizeof(buf1)); if (size < 0) return; if (size == 0) { /* end of connection */ qemu_set_fd_handler(s->fd, NULL, NULL, NULL); return; } buf = buf1; while (size > 0) { /* reassemble a packet from the network */ switch(s->state) { case 0: l = 4 - s->index; if (l > size) l = size; memcpy(s->buf + s->index, buf, l); buf += l; size -= l; s->index += l; if (s->index == 4) { /* got length */ s->packet_len = ntohl(*(uint32_t *)s->buf); s->index = 0; s->state = 1; } break; case 1: l = s->packet_len - s->index; if (l > size) l = size; memcpy(s->buf + s->index, buf, l); s->index += l; buf += l; size -= l; if (s->index >= s->packet_len) { qemu_send_packet(s->vc, s->buf, s->packet_len); s->index = 0; s->state = 0; } break; } } } static void net_socket_send_dgram(void *opaque) { NetSocketState *s = opaque; int size; size = recv(s->fd, s->buf, sizeof(s->buf), 0); if (size < 0) return; if (size == 0) { /* end of connection */ qemu_set_fd_handler(s->fd, NULL, NULL, NULL); return; } qemu_send_packet(s->vc, s->buf, size); } static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) { struct ip_mreq imr; int fd; int val, ret; if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", inet_ntoa(mcastaddr->sin_addr), ntohl(mcastaddr->sin_addr.s_addr)); return -1; } fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket(PF_INET, SOCK_DGRAM)"); return -1; } /* Add host to multicast group */ imr.imr_multiaddr = mcastaddr->sin_addr; imr.imr_interface.s_addr = htonl(INADDR_ANY); ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *) &imr, sizeof(struct ip_mreq)); if (ret < 0) { perror("setsockopt(IP_ADD_MEMBERSHIP)"); goto fail; } /* Force mcast msgs to loopback (eg. several QEMUs in same host */ val = 1; ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)); if (ret < 0) { perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); goto fail; } ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (ret < 0) { perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); goto fail; } ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); if (ret < 0) { perror("bind"); goto fail; } fcntl(fd, F_SETFL, O_NONBLOCK); return fd; fail: if (fd>=0) close(fd); return -1; } static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd, int is_connected) { struct sockaddr_in saddr; int newfd; socklen_t saddr_len; NetSocketState *s; /* fd passed: multicast: "learn" dgram_dst address from bound address and save it * Because this may be "shared" socket from a "master" process, datagrams would be recv() * by ONLY ONE process: we must "clone" this dgram socket --jjo */ if (is_connected) { if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { /* must be bound */ if (saddr.sin_addr.s_addr==0) { fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", fd); return NULL; } /* clone dgram socket */ newfd = net_socket_mcast_create(&saddr); if (newfd < 0) { /* error already reported by net_socket_mcast_create() */ close(fd); return NULL; } /* clone newfd to fd, close newfd */ dup2(newfd, fd); close(newfd); } else { fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", fd, strerror(errno)); return NULL; } } s = qemu_mallocz(sizeof(NetSocketState)); if (!s) return NULL; s->fd = fd; s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, s); qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s); /* mcast: save bound address as dst */ if (is_connected) s->dgram_dst=saddr; snprintf(s->vc->info_str, sizeof(s->vc->info_str), "socket: fd=%d (%s mcast=%s:%d)", fd, is_connected? "cloned" : "", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return s; } static void net_socket_connect(void *opaque) { NetSocketState *s = opaque; qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); } static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd, int is_connected) { NetSocketState *s; s = qemu_mallocz(sizeof(NetSocketState)); if (!s) return NULL; s->fd = fd; s->vc = qemu_new_vlan_client(vlan, net_socket_receive, s); snprintf(s->vc->info_str, sizeof(s->vc->info_str), "socket: fd=%d", fd); if (is_connected) { net_socket_connect(s); } else { qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); } return s; } static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, int is_connected) { int so_type=-1, optlen=sizeof(so_type); if(getsockopt(fd, SOL_SOCKET,SO_TYPE, &so_type, &optlen)< 0) { fprintf(stderr, "qemu: error: setsockopt(SO_TYPE) for fd=%d failed\n", fd); return NULL; } switch(so_type) { case SOCK_DGRAM: return net_socket_fd_init_dgram(vlan, fd, is_connected); case SOCK_STREAM: return net_socket_fd_init_stream(vlan, fd, is_connected); default: /* who knows ... this could be a eg. a pty, do warn and continue as stream */ fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd); return net_socket_fd_init_stream(vlan, fd, is_connected); } return NULL; } static void net_socket_accept(void *opaque) { NetSocketListenState *s = opaque; NetSocketState *s1; struct sockaddr_in saddr; socklen_t len; int fd; for(;;) { len = sizeof(saddr); fd = accept(s->fd, (struct sockaddr *)&saddr, &len); if (fd < 0 && errno != EINTR) { return; } else if (fd >= 0) { break; } } s1 = net_socket_fd_init(s->vlan, fd, 1); if (!s1) { close(fd); } else { snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), "socket: connection from %s:%d", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } } static int net_socket_listen_init(VLANState *vlan, const char *host_str) { NetSocketListenState *s; int fd, val, ret; struct sockaddr_in saddr; if (parse_host_port(&saddr, host_str) < 0) return -1; s = qemu_mallocz(sizeof(NetSocketListenState)); if (!s) return -1; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } fcntl(fd, F_SETFL, O_NONBLOCK); /* allow fast reuse */ val = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { perror("bind"); return -1; } ret = listen(fd, 0); if (ret < 0) { perror("listen"); return -1; } s->vlan = vlan; s->fd = fd; qemu_set_fd_handler(fd, net_socket_accept, NULL, s); return 0; } static int net_socket_connect_init(VLANState *vlan, const char *host_str) { NetSocketState *s; int fd, connected, ret; struct sockaddr_in saddr; if (parse_host_port(&saddr, host_str) < 0) return -1; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } fcntl(fd, F_SETFL, O_NONBLOCK); connected = 0; for(;;) { ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { if (errno == EINTR || errno == EAGAIN) { } else if (errno == EINPROGRESS) { break; } else { perror("connect"); close(fd); return -1; } } else { connected = 1; break; } } s = net_socket_fd_init(vlan, fd, connected); if (!s) return -1; snprintf(s->vc->info_str, sizeof(s->vc->info_str), "socket: connect to %s:%d", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; } static int net_socket_mcast_init(VLANState *vlan, const char *host_str) { NetSocketState *s; int fd; struct sockaddr_in saddr; if (parse_host_port(&saddr, host_str) < 0) return -1; fd = net_socket_mcast_create(&saddr); if (fd < 0) return -1; s = net_socket_fd_init(vlan, fd, 0); if (!s) return -1; s->dgram_dst = saddr; snprintf(s->vc->info_str, sizeof(s->vc->info_str), "socket: mcast=%s:%d", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; } #endif /* !_WIN32 */ static int get_param_value(char *buf, int buf_size, const char *tag, const char *str) { const char *p; char *q; char option[128]; p = str; for(;;) { q = option; while (*p != '\0' && *p != '=') { if ((q - option) < sizeof(option) - 1) *q++ = *p; p++; } *q = '\0'; if (*p != '=') break; p++; if (!strcmp(tag, option)) { q = buf; while (*p != '\0' && *p != ',') { if ((q - buf) < buf_size - 1) *q++ = *p; p++; } *q = '\0'; return q - buf; } else { while (*p != '\0' && *p != ',') { p++; } } if (*p != ',') break; p++; } return 0; } int net_client_init(const char *str) { const char *p; char *q; char device[64]; char buf[1024]; int vlan_id, ret; VLANState *vlan; p = str; q = device; while (*p != '\0' && *p != ',') { if ((q - device) < sizeof(device) - 1) *q++ = *p; p++; } *q = '\0'; if (*p == ',') p++; vlan_id = 0; if (get_param_value(buf, sizeof(buf), "vlan", p)) { vlan_id = strtol(buf, NULL, 0); } vlan = qemu_find_vlan(vlan_id); if (!vlan) { fprintf(stderr, "Could not create vlan %d\n", vlan_id); return -1; } if (!strcmp(device, "nic")) { NICInfo *nd; uint8_t *macaddr; if (nb_nics >= MAX_NICS) { fprintf(stderr, "Too Many NICs\n"); return -1; } nd = &nd_table[nb_nics]; macaddr = nd->macaddr; macaddr[0] = 0x52; macaddr[1] = 0x54; macaddr[2] = 0x00; macaddr[3] = 0x12; macaddr[4] = 0x34; macaddr[5] = 0x56 + nb_nics; if (get_param_value(buf, sizeof(buf), "macaddr", p)) { if (parse_macaddr(macaddr, buf) < 0) { fprintf(stderr, "invalid syntax for ethernet address\n"); return -1; } } nd->vlan = vlan; nb_nics++; ret = 0; } else if (!strcmp(device, "none")) { /* does nothing. It is needed to signal that no network cards are wanted */ ret = 0; } else #ifdef CONFIG_SLIRP if (!strcmp(device, "user")) { ret = net_slirp_init(vlan); } else #endif #ifndef _WIN32 if (!strcmp(device, "tap")) { char ifname[64]; char setup_script[1024]; int fd; if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { fd = strtol(buf, NULL, 0); ret = -1; if (net_tap_fd_init(vlan, fd)) ret = 0; } else { get_param_value(ifname, sizeof(ifname), "ifname", p); if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) { pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT); } ret = net_tap_init(vlan, ifname, setup_script); } } else if (!strcmp(device, "socket")) { if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { int fd; fd = strtol(buf, NULL, 0); ret = -1; if (net_socket_fd_init(vlan, fd, 1)) ret = 0; } else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) { ret = net_socket_listen_init(vlan, buf); } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) { ret = net_socket_connect_init(vlan, buf); } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) { ret = net_socket_mcast_init(vlan, buf); } else { fprintf(stderr, "Unknown socket options: %s\n", p); return -1; } } else #endif { fprintf(stderr, "Unknown network device: %s\n", device); return -1; } if (ret < 0) { fprintf(stderr, "Could not initialize device '%s'\n", device); } return ret; } void do_info_network(void) { VLANState *vlan; VLANClientState *vc; for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) { term_printf("VLAN %d devices:\n", vlan->id); for(vc = vlan->first_client; vc != NULL; vc = vc->next) term_printf(" %s\n", vc->info_str); } } /***********************************************************/ /* USB devices */ static int usb_device_add(const char *devname) { const char *p; USBDevice *dev; int i; if (!vm_usb_hub) return -1; for(i = 0;i < MAX_VM_USB_PORTS; i++) { if (!vm_usb_ports[i]->dev) break; } if (i == MAX_VM_USB_PORTS) return -1; if (strstart(devname, "host:", &p)) { dev = usb_host_device_open(p); if (!dev) return -1; } else if (!strcmp(devname, "mouse")) { dev = usb_mouse_init(); if (!dev) return -1; } else { return -1; } usb_attach(vm_usb_ports[i], dev); return 0; } static int usb_device_del(const char *devname) { USBDevice *dev; int bus_num, addr, i; const char *p; if (!vm_usb_hub) return -1; p = strchr(devname, '.'); if (!p) return -1; bus_num = strtoul(devname, NULL, 0); addr = strtoul(p + 1, NULL, 0); if (bus_num != 0) return -1; for(i = 0;i < MAX_VM_USB_PORTS; i++) { dev = vm_usb_ports[i]->dev; if (dev && dev->addr == addr) break; } if (i == MAX_VM_USB_PORTS) return -1; usb_attach(vm_usb_ports[i], NULL); return 0; } void do_usb_add(const char *devname) { int ret; ret = usb_device_add(devname); if (ret < 0) term_printf("Could not add USB device '%s'\n", devname); } void do_usb_del(const char *devname) { int ret; ret = usb_device_del(devname); if (ret < 0) term_printf("Could not remove USB device '%s'\n", devname); } void usb_info(void) { USBDevice *dev; int i; const char *speed_str; if (!vm_usb_hub) { term_printf("USB support not enabled\n"); return; } for(i = 0; i < MAX_VM_USB_PORTS; i++) { dev = vm_usb_ports[i]->dev; if (dev) { term_printf("Hub port %d:\n", i); switch(dev->speed) { case USB_SPEED_LOW: speed_str = "1.5"; break; case USB_SPEED_FULL: speed_str = "12"; break; case USB_SPEED_HIGH: speed_str = "480"; break; default: speed_str = "?"; break; } term_printf(" Device %d.%d, speed %s Mb/s\n", 0, dev->addr, speed_str); } } } /***********************************************************/ /* pid file */ static char *pid_filename; /* Remove PID file. Called on normal exit */ static void remove_pidfile(void) { unlink (pid_filename); } static void create_pidfile(const char *filename) { struct stat pidstat; FILE *f; /* Try to write our PID to the named file */ if (stat(filename, &pidstat) < 0) { if (errno == ENOENT) { if ((f = fopen (filename, "w")) == NULL) { perror("Opening pidfile"); exit(1); } fprintf(f, "%d\n", getpid()); fclose(f); pid_filename = qemu_strdup(filename); if (!pid_filename) { fprintf(stderr, "Could not save PID filename"); exit(1); } atexit(remove_pidfile); } } else { fprintf(stderr, "%s already exists. Remove it and try again.\n", filename); exit(1); } } /***********************************************************/ /* dumb display */ static void dumb_update(DisplayState *ds, int x, int y, int w, int h) { } static void dumb_resize(DisplayState *ds, int w, int h) { } static void dumb_refresh(DisplayState *ds) { vga_update_display(); } void dumb_display_init(DisplayState *ds) { ds->data = NULL; ds->linesize = 0; ds->depth = 0; ds->dpy_update = dumb_update; ds->dpy_resize = dumb_resize; ds->dpy_refresh = dumb_refresh; } #if !defined(CONFIG_SOFTMMU) /***********************************************************/ /* cpu signal handler */ static void host_segv_handler(int host_signum, siginfo_t *info, void *puc) { if (cpu_signal_handler(host_signum, info, puc)) return; if (stdio_nb_clients > 0) term_exit(); abort(); } #endif /***********************************************************/ /* I/O handling */ #define MAX_IO_HANDLERS 64 typedef struct IOHandlerRecord { int fd; IOCanRWHandler *fd_read_poll; IOHandler *fd_read; IOHandler *fd_write; void *opaque; /* temporary data */ struct pollfd *ufd; struct IOHandlerRecord *next; } IOHandlerRecord; static IOHandlerRecord *first_io_handler; /* XXX: fd_read_poll should be suppressed, but an API change is necessary in the character devices to suppress fd_can_read(). */ int qemu_set_fd_handler2(int fd, IOCanRWHandler *fd_read_poll, IOHandler *fd_read, IOHandler *fd_write, void *opaque) { IOHandlerRecord **pioh, *ioh; if (!fd_read && !fd_write) { pioh = &first_io_handler; for(;;) { ioh = *pioh; if (ioh == NULL) break; if (ioh->fd == fd) { *pioh = ioh->next; break; } pioh = &ioh->next; } } else { for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { if (ioh->fd == fd) goto found; } ioh = qemu_mallocz(sizeof(IOHandlerRecord)); if (!ioh) return -1; ioh->next = first_io_handler; first_io_handler = ioh; found: ioh->fd = fd; ioh->fd_read_poll = fd_read_poll; ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; } return 0; } int qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) { return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); } /***********************************************************/ /* savevm/loadvm support */ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) { fwrite(buf, 1, size, f); } void qemu_put_byte(QEMUFile *f, int v) { fputc(v, f); } void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); qemu_put_byte(f, v); } void qemu_put_be32(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 24); qemu_put_byte(f, v >> 16); qemu_put_byte(f, v >> 8); qemu_put_byte(f, v); } void qemu_put_be64(QEMUFile *f, uint64_t v) { qemu_put_be32(f, v >> 32); qemu_put_be32(f, v); } int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) { return fread(buf, 1, size, f); } int qemu_get_byte(QEMUFile *f) { int v; v = fgetc(f); if (v == EOF) return 0; else return v; } unsigned int qemu_get_be16(QEMUFile *f) { unsigned int v; v = qemu_get_byte(f) << 8; v |= qemu_get_byte(f); return v; } unsigned int qemu_get_be32(QEMUFile *f) { unsigned int v; v = qemu_get_byte(f) << 24; v |= qemu_get_byte(f) << 16; v |= qemu_get_byte(f) << 8; v |= qemu_get_byte(f); return v; } uint64_t qemu_get_be64(QEMUFile *f) { uint64_t v; v = (uint64_t)qemu_get_be32(f) << 32; v |= qemu_get_be32(f); return v; } int64_t qemu_ftell(QEMUFile *f) { return ftell(f); } int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) { if (fseek(f, pos, whence) < 0) return -1; return ftell(f); } typedef struct SaveStateEntry { char idstr[256]; int instance_id; int version_id; SaveStateHandler *save_state; LoadStateHandler *load_state; void *opaque; struct SaveStateEntry *next; } SaveStateEntry; static SaveStateEntry *first_se; int register_savevm(const char *idstr, int instance_id, int version_id, SaveStateHandler *save_state, LoadStateHandler *load_state, void *opaque) { SaveStateEntry *se, **pse; se = qemu_malloc(sizeof(SaveStateEntry)); if (!se) return -1; pstrcpy(se->idstr, sizeof(se->idstr), idstr); se->instance_id = instance_id; se->version_id = version_id; se->save_state = save_state; se->load_state = load_state; se->opaque = opaque; se->next = NULL; /* add at the end of list */ pse = &first_se; while (*pse != NULL) pse = &(*pse)->next; *pse = se; return 0; } #define QEMU_VM_FILE_MAGIC 0x5145564d #define QEMU_VM_FILE_VERSION 0x00000001 int qemu_savevm(const char *filename) { SaveStateEntry *se; QEMUFile *f; int len, len_pos, cur_pos, saved_vm_running, ret; saved_vm_running = vm_running; vm_stop(0); f = fopen(filename, "wb"); if (!f) { ret = -1; goto the_end; } qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); for(se = first_se; se != NULL; se = se->next) { /* ID string */ len = strlen(se->idstr); qemu_put_byte(f, len); qemu_put_buffer(f, se->idstr, len); qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); /* record size: filled later */ len_pos = ftell(f); qemu_put_be32(f, 0); se->save_state(f, se->opaque); /* fill record size */ cur_pos = ftell(f); len = ftell(f) - len_pos - 4; fseek(f, len_pos, SEEK_SET); qemu_put_be32(f, len); fseek(f, cur_pos, SEEK_SET); } fclose(f); ret = 0; the_end: if (saved_vm_running) vm_start(); return ret; } static SaveStateEntry *find_se(const char *idstr, int instance_id) { SaveStateEntry *se; for(se = first_se; se != NULL; se = se->next) { if (!strcmp(se->idstr, idstr) && instance_id == se->instance_id) return se; } return NULL; } int qemu_loadvm(const char *filename) { SaveStateEntry *se; QEMUFile *f; int len, cur_pos, ret, instance_id, record_len, version_id; int saved_vm_running; unsigned int v; char idstr[256]; saved_vm_running = vm_running; vm_stop(0); f = fopen(filename, "rb"); if (!f) { ret = -1; goto the_end; } v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) goto fail; v = qemu_get_be32(f); if (v != QEMU_VM_FILE_VERSION) { fail: fclose(f); ret = -1; goto the_end; } for(;;) { len = qemu_get_byte(f); if (feof(f)) break; qemu_get_buffer(f, idstr, len); idstr[len] = '\0'; instance_id = qemu_get_be32(f); version_id = qemu_get_be32(f); record_len = qemu_get_be32(f); #if 0 printf("idstr=%s instance=0x%x version=%d len=%d\n", idstr, instance_id, version_id, record_len); #endif cur_pos = ftell(f); se = find_se(idstr, instance_id); if (!se) { fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", instance_id, idstr); } else { ret = se->load_state(f, se->opaque, version_id); if (ret < 0) { fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", instance_id, idstr); } } /* always seek to exact end of record */ qemu_fseek(f, cur_pos + record_len, SEEK_SET); } fclose(f); ret = 0; the_end: if (saved_vm_running) vm_start(); return ret; } /***********************************************************/ /* cpu save/restore */ #if defined(TARGET_I386) static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) { qemu_put_be32(f, dt->selector); qemu_put_betl(f, dt->base); qemu_put_be32(f, dt->limit); qemu_put_be32(f, dt->flags); } static void cpu_get_seg(QEMUFile *f, SegmentCache *dt) { dt->selector = qemu_get_be32(f); dt->base = qemu_get_betl(f); dt->limit = qemu_get_be32(f); dt->flags = qemu_get_be32(f); } void cpu_save(QEMUFile *f, void *opaque) { CPUState *env = opaque; uint16_t fptag, fpus, fpuc, fpregs_format; uint32_t hflags; int i; for(i = 0; i < CPU_NB_REGS; i++) qemu_put_betls(f, &env->regs[i]); qemu_put_betls(f, &env->eip); qemu_put_betls(f, &env->eflags); hflags = env->hflags; /* XXX: suppress most of the redundant hflags */ qemu_put_be32s(f, &hflags); /* FPU */ fpuc = env->fpuc; fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; for(i = 0; i < 8; i++) { fptag |= ((!env->fptags[i]) << i); } qemu_put_be16s(f, &fpuc); qemu_put_be16s(f, &fpus); qemu_put_be16s(f, &fptag); #ifdef USE_X86LDOUBLE fpregs_format = 0; #else fpregs_format = 1; #endif qemu_put_be16s(f, &fpregs_format); for(i = 0; i < 8; i++) { #ifdef USE_X86LDOUBLE { uint64_t mant; uint16_t exp; /* we save the real CPU data (in case of MMX usage only 'mant' contains the MMX register */ cpu_get_fp80(&mant, &exp, env->fpregs[i].d); qemu_put_be64(f, mant); qemu_put_be16(f, exp); } #else /* if we use doubles for float emulation, we save the doubles to avoid losing information in case of MMX usage. It can give problems if the image is restored on a CPU where long doubles are used instead. */ qemu_put_be64(f, env->fpregs[i].mmx.MMX_Q(0)); #endif } for(i = 0; i < 6; i++) cpu_put_seg(f, &env->segs[i]); cpu_put_seg(f, &env->ldt); cpu_put_seg(f, &env->tr); cpu_put_seg(f, &env->gdt); cpu_put_seg(f, &env->idt); qemu_put_be32s(f, &env->sysenter_cs); qemu_put_be32s(f, &env->sysenter_esp); qemu_put_be32s(f, &env->sysenter_eip); qemu_put_betls(f, &env->cr[0]); qemu_put_betls(f, &env->cr[2]); qemu_put_betls(f, &env->cr[3]); qemu_put_betls(f, &env->cr[4]); for(i = 0; i < 8; i++) qemu_put_betls(f, &env->dr[i]); /* MMU */ qemu_put_be32s(f, &env->a20_mask); /* XMM */ qemu_put_be32s(f, &env->mxcsr); for(i = 0; i < CPU_NB_REGS; i++) { qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(0)); qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(1)); } #ifdef TARGET_X86_64 qemu_put_be64s(f, &env->efer); qemu_put_be64s(f, &env->star); qemu_put_be64s(f, &env->lstar); qemu_put_be64s(f, &env->cstar); qemu_put_be64s(f, &env->fmask); qemu_put_be64s(f, &env->kernelgsbase); #endif } #ifdef USE_X86LDOUBLE /* XXX: add that in a FPU generic layer */ union x86_longdouble { uint64_t mant; uint16_t exp; }; #define MANTD1(fp) (fp & ((1LL << 52) - 1)) #define EXPBIAS1 1023 #define EXPD1(fp) ((fp >> 52) & 0x7FF) #define SIGND1(fp) ((fp >> 32) & 0x80000000) static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp) { int e; /* mantissa */ p->mant = (MANTD1(temp) << 11) | (1LL << 63); /* exponent + sign */ e = EXPD1(temp) - EXPBIAS1 + 16383; e |= SIGND1(temp) >> 16; p->exp = e; } #endif int cpu_load(QEMUFile *f, void *opaque, int version_id) { CPUState *env = opaque; int i, guess_mmx; uint32_t hflags; uint16_t fpus, fpuc, fptag, fpregs_format; if (version_id != 3) return -EINVAL; for(i = 0; i < CPU_NB_REGS; i++) qemu_get_betls(f, &env->regs[i]); qemu_get_betls(f, &env->eip); qemu_get_betls(f, &env->eflags); qemu_get_be32s(f, &hflags); qemu_get_be16s(f, &fpuc); qemu_get_be16s(f, &fpus); qemu_get_be16s(f, &fptag); qemu_get_be16s(f, &fpregs_format); /* NOTE: we cannot always restore the FPU state if the image come from a host with a different 'USE_X86LDOUBLE' define. We guess if we are in an MMX state to restore correctly in that case. */ guess_mmx = ((fptag == 0xff) && (fpus & 0x3800) == 0); for(i = 0; i < 8; i++) { uint64_t mant; uint16_t exp; switch(fpregs_format) { case 0: mant = qemu_get_be64(f); exp = qemu_get_be16(f); #ifdef USE_X86LDOUBLE env->fpregs[i].d = cpu_set_fp80(mant, exp); #else /* difficult case */ if (guess_mmx) env->fpregs[i].mmx.MMX_Q(0) = mant; else env->fpregs[i].d = cpu_set_fp80(mant, exp); #endif break; case 1: mant = qemu_get_be64(f); #ifdef USE_X86LDOUBLE { union x86_longdouble *p; /* difficult case */ p = (void *)&env->fpregs[i]; if (guess_mmx) { p->mant = mant; p->exp = 0xffff; } else { fp64_to_fp80(p, mant); } } #else env->fpregs[i].mmx.MMX_Q(0) = mant; #endif break; default: return -EINVAL; } } env->fpuc = fpuc; /* XXX: restore FPU round state */ env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; fptag ^= 0xff; for(i = 0; i < 8; i++) { env->fptags[i] = (fptag >> i) & 1; } for(i = 0; i < 6; i++) cpu_get_seg(f, &env->segs[i]); cpu_get_seg(f, &env->ldt); cpu_get_seg(f, &env->tr); cpu_get_seg(f, &env->gdt); cpu_get_seg(f, &env->idt); qemu_get_be32s(f, &env->sysenter_cs); qemu_get_be32s(f, &env->sysenter_esp); qemu_get_be32s(f, &env->sysenter_eip); qemu_get_betls(f, &env->cr[0]); qemu_get_betls(f, &env->cr[2]); qemu_get_betls(f, &env->cr[3]); qemu_get_betls(f, &env->cr[4]); for(i = 0; i < 8; i++) qemu_get_betls(f, &env->dr[i]); /* MMU */ qemu_get_be32s(f, &env->a20_mask); qemu_get_be32s(f, &env->mxcsr); for(i = 0; i < CPU_NB_REGS; i++) { qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(0)); qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(1)); } #ifdef TARGET_X86_64 qemu_get_be64s(f, &env->efer); qemu_get_be64s(f, &env->star); qemu_get_be64s(f, &env->lstar); qemu_get_be64s(f, &env->cstar); qemu_get_be64s(f, &env->fmask); qemu_get_be64s(f, &env->kernelgsbase); #endif /* XXX: compute hflags from scratch, except for CPL and IIF */ env->hflags = hflags; tlb_flush(env, 1); return 0; } #elif defined(TARGET_PPC) void cpu_save(QEMUFile *f, void *opaque) { } int cpu_load(QEMUFile *f, void *opaque, int version_id) { return 0; } #elif defined(TARGET_MIPS) void cpu_save(QEMUFile *f, void *opaque) { } int cpu_load(QEMUFile *f, void *opaque, int version_id) { return 0; } #elif defined(TARGET_SPARC) void cpu_save(QEMUFile *f, void *opaque) { CPUState *env = opaque; int i; uint32_t tmp; for(i = 0; i < 8; i++) qemu_put_betls(f, &env->gregs[i]); for(i = 0; i < NWINDOWS * 16; i++) qemu_put_betls(f, &env->regbase[i]); /* FPU */ for(i = 0; i < TARGET_FPREGS; i++) { union { TARGET_FPREG_T f; target_ulong i; } u; u.f = env->fpr[i]; qemu_put_betl(f, u.i); } qemu_put_betls(f, &env->pc); qemu_put_betls(f, &env->npc); qemu_put_betls(f, &env->y); tmp = GET_PSR(env); qemu_put_be32(f, tmp); qemu_put_betls(f, &env->fsr); qemu_put_betls(f, &env->tbr); #ifndef TARGET_SPARC64 qemu_put_be32s(f, &env->wim); /* MMU */ for(i = 0; i < 16; i++) qemu_put_be32s(f, &env->mmuregs[i]); #endif } int cpu_load(QEMUFile *f, void *opaque, int version_id) { CPUState *env = opaque; int i; uint32_t tmp; for(i = 0; i < 8; i++) qemu_get_betls(f, &env->gregs[i]); for(i = 0; i < NWINDOWS * 16; i++) qemu_get_betls(f, &env->regbase[i]); /* FPU */ for(i = 0; i < TARGET_FPREGS; i++) { union { TARGET_FPREG_T f; target_ulong i; } u; u.i = qemu_get_betl(f); env->fpr[i] = u.f; } qemu_get_betls(f, &env->pc); qemu_get_betls(f, &env->npc); qemu_get_betls(f, &env->y); tmp = qemu_get_be32(f); env->cwp = 0; /* needed to ensure that the wrapping registers are correctly updated */ PUT_PSR(env, tmp); qemu_get_betls(f, &env->fsr); qemu_get_betls(f, &env->tbr); #ifndef TARGET_SPARC64 qemu_get_be32s(f, &env->wim); /* MMU */ for(i = 0; i < 16; i++) qemu_get_be32s(f, &env->mmuregs[i]); #endif tlb_flush(env, 1); return 0; } #elif defined(TARGET_ARM) /* ??? Need to implement these. */ void cpu_save(QEMUFile *f, void *opaque) { } int cpu_load(QEMUFile *f, void *opaque, int version_id) { return 0; } #else #warning No CPU save/restore functions #endif /***********************************************************/ /* ram save/restore */ /* we just avoid storing empty pages */ static void ram_put_page(QEMUFile *f, const uint8_t *buf, int len) { int i, v; v = buf[0]; for(i = 1; i < len; i++) { if (buf[i] != v) goto normal_save; } qemu_put_byte(f, 1); qemu_put_byte(f, v); return; normal_save: qemu_put_byte(f, 0); qemu_put_buffer(f, buf, len); } static int ram_get_page(QEMUFile *f, uint8_t *buf, int len) { int v; v = qemu_get_byte(f); switch(v) { case 0: if (qemu_get_buffer(f, buf, len) != len) return -EIO; break; case 1: v = qemu_get_byte(f); memset(buf, v, len); break; default: return -EINVAL; } return 0; } static void ram_save(QEMUFile *f, void *opaque) { int i; qemu_put_be32(f, phys_ram_size); for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) { ram_put_page(f, phys_ram_base + i, TARGET_PAGE_SIZE); } } static int ram_load(QEMUFile *f, void *opaque, int version_id) { int i, ret; if (version_id != 1) return -EINVAL; if (qemu_get_be32(f) != phys_ram_size) return -EINVAL; for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) { ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE); if (ret) return ret; } return 0; } /***********************************************************/ /* machine registration */ QEMUMachine *first_machine = NULL; int qemu_register_machine(QEMUMachine *m) { QEMUMachine **pm; pm = &first_machine; while (*pm != NULL) pm = &(*pm)->next; m->next = NULL; *pm = m; return 0; } QEMUMachine *find_machine(const char *name) { QEMUMachine *m; for(m = first_machine; m != NULL; m = m->next) { if (!strcmp(m->name, name)) return m; } return NULL; } /***********************************************************/ /* main execution loop */ void gui_update(void *opaque) { display_state.dpy_refresh(&display_state); qemu_mod_timer(gui_timer, GUI_REFRESH_INTERVAL + qemu_get_clock(rt_clock)); } struct vm_change_state_entry { VMChangeStateHandler *cb; void *opaque; LIST_ENTRY (vm_change_state_entry) entries; }; static LIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque) { VMChangeStateEntry *e; e = qemu_mallocz(sizeof (*e)); if (!e) return NULL; e->cb = cb; e->opaque = opaque; LIST_INSERT_HEAD(&vm_change_state_head, e, entries); return e; } void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) { LIST_REMOVE (e, entries); qemu_free (e); } static void vm_state_notify(int running) { VMChangeStateEntry *e; for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) { e->cb(e->opaque, running); } } /* XXX: support several handlers */ static VMStopHandler *vm_stop_cb; static void *vm_stop_opaque; int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque) { vm_stop_cb = cb; vm_stop_opaque = opaque; return 0; } void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque) { vm_stop_cb = NULL; } void vm_start(void) { if (!vm_running) { cpu_enable_ticks(); vm_running = 1; vm_state_notify(1); } } void vm_stop(int reason) { if (vm_running) { cpu_disable_ticks(); vm_running = 0; if (reason != 0) { if (vm_stop_cb) { vm_stop_cb(vm_stop_opaque, reason); } } vm_state_notify(0); } } /* reset/shutdown handler */ typedef struct QEMUResetEntry { QEMUResetHandler *func; void *opaque; struct QEMUResetEntry *next; } QEMUResetEntry; static QEMUResetEntry *first_reset_entry; static int reset_requested; static int shutdown_requested; static int powerdown_requested; void qemu_register_reset(QEMUResetHandler *func, void *opaque) { QEMUResetEntry **pre, *re; pre = &first_reset_entry; while (*pre != NULL) pre = &(*pre)->next; re = qemu_mallocz(sizeof(QEMUResetEntry)); re->func = func; re->opaque = opaque; re->next = NULL; *pre = re; } void qemu_system_reset(void) { QEMUResetEntry *re; /* reset all devices */ for(re = first_reset_entry; re != NULL; re = re->next) { re->func(re->opaque); } } void qemu_system_reset_request(void) { reset_requested = 1; if (cpu_single_env) cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_shutdown_request(void) { shutdown_requested = 1; if (cpu_single_env) cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_powerdown_request(void) { powerdown_requested = 1; if (cpu_single_env) cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void main_loop_wait(int timeout) { #ifndef _WIN32 struct pollfd ufds[MAX_IO_HANDLERS + 1], *pf; IOHandlerRecord *ioh, *ioh_next; #endif int ret; #ifdef _WIN32 if (timeout > 0) Sleep(timeout); #else /* poll any events */ /* XXX: separate device handlers from system ones */ pf = ufds; for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { pf->events = 0; pf->fd = ioh->fd; if (ioh->fd_read && (!ioh->fd_read_poll || ioh->fd_read_poll(ioh->opaque) != 0)) { pf->events |= POLLIN; } if (ioh->fd_write) { pf->events |= POLLOUT; } ioh->ufd = pf; pf++; } ret = poll(ufds, pf - ufds, timeout); if (ret > 0) { /* XXX: better handling of removal */ for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { ioh_next = ioh->next; pf = ioh->ufd; if (pf->revents & POLLIN) { ioh->fd_read(ioh->opaque); } if (pf->revents & POLLOUT) { ioh->fd_write(ioh->opaque); } } } #endif /* !defined(_WIN32) */ #if defined(CONFIG_SLIRP) /* XXX: merge with poll() */ if (slirp_inited) { fd_set rfds, wfds, xfds; int nfds; struct timeval tv; nfds = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); slirp_select_fill(&nfds, &rfds, &wfds, &xfds); tv.tv_sec = 0; tv.tv_usec = 0; ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); if (ret >= 0) { slirp_select_poll(&rfds, &wfds, &xfds); } } #endif if (vm_running) { qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL], qemu_get_clock(vm_clock)); /* run dma transfers, if any */ DMA_run(); } /* real time timers */ qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME], qemu_get_clock(rt_clock)); } static CPUState *cur_cpu; int main_loop(void) { int ret, timeout; CPUState *env; cur_cpu = first_cpu; for(;;) { if (vm_running) { env = cur_cpu; for(;;) { /* get next cpu */ env = env->next_cpu; if (!env) env = first_cpu; ret = cpu_exec(env); if (ret != EXCP_HALTED) break; /* all CPUs are halted ? */ if (env == cur_cpu) { ret = EXCP_HLT; break; } } cur_cpu = env; if (shutdown_requested) { ret = EXCP_INTERRUPT; break; } if (reset_requested) { reset_requested = 0; qemu_system_reset(); ret = EXCP_INTERRUPT; } if (powerdown_requested) { powerdown_requested = 0; qemu_system_powerdown(); ret = EXCP_INTERRUPT; } if (ret == EXCP_DEBUG) { vm_stop(EXCP_DEBUG); } /* if hlt instruction, we wait until the next IRQ */ /* XXX: use timeout computed from timers */ if (ret == EXCP_HLT) timeout = 10; else timeout = 0; } else { timeout = 10; } main_loop_wait(timeout); } cpu_disable_ticks(); return ret; } void help(void) { printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n" "usage: %s [options] [disk_image]\n" "\n" "'disk_image' is a raw hard image image for IDE hard disk 0\n" "\n" "Standard options:\n" "-M machine select emulated machine (-M ? for list)\n" "-fda/-fdb file use 'file' as floppy disk 0/1 image\n" "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n" "-boot [a|c|d] boot on floppy (a), hard disk (c) or CD-ROM (d)\n" "-snapshot write to temporary files instead of disk image files\n" "-m megs set virtual RAM size to megs MB [default=%d]\n" "-smp n set the number of CPUs to 'n' [default=1]\n" "-nographic disable graphical output and redirect serial I/Os to console\n" #ifndef _WIN32 "-k language use keyboard layout (for example \"fr\" for French)\n" #endif #ifdef HAS_AUDIO "-enable-audio enable audio support, and all the sound cards\n" "-audio-help print list of audio drivers and their options\n" "-soundhw c1,... enable audio support\n" " and only specified sound cards (comma separated list)\n" " use -soundhw ? to get the list of supported cards\n" #endif "-localtime set the real time clock to local time [default=utc]\n" "-full-screen start in full screen\n" #ifdef TARGET_I386 "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n" #endif "-usb enable the USB driver (will be the default soon)\n" "-usbdevice name add the host or guest USB device 'name'\n" #if defined(TARGET_PPC) || defined(TARGET_SPARC) "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n" #endif "\n" "Network options:\n" "-net nic[,vlan=n][,macaddr=addr]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP "-net user[,vlan=n]\n" " connect the user mode network stack to VLAN 'n'\n" #endif #ifndef _WIN32 "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file]\n" " connect the host TAP network interface to VLAN 'n' and use\n" " the network script 'file' (default=%s);\n" " use 'fd=h' to connect to an already opened TAP interface\n" "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n" " connect the vlan 'n' to another VLAN using a socket connection\n" "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n" " connect the vlan 'n' to multicast maddr and port\n" #endif "-net none use it alone to have zero network devices; if no -net option\n" " is provided, the default is '-net nic -net user'\n" "\n" #ifdef CONFIG_SLIRP "-tftp prefix allow tftp access to files starting with prefix [-net user]\n" #ifndef _WIN32 "-smb dir allow SMB access to files in 'dir' [-net user]\n" #endif "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n" " redirect TCP or UDP connections from host to guest [-net user]\n" #endif "\n" "Linux boot specific:\n" "-kernel bzImage use 'bzImage' as kernel image\n" "-append cmdline use 'cmdline' as kernel command line\n" "-initrd file use 'file' as initial ram disk\n" "\n" "Debug/Expert options:\n" "-monitor dev redirect the monitor to char device 'dev'\n" "-serial dev redirect the serial port to char device 'dev'\n" "-parallel dev redirect the parallel port to char device 'dev'\n" "-pidfile file Write PID to 'file'\n" "-S freeze CPU at startup (use 'c' to start execution)\n" "-s wait gdb connection to port %d\n" "-p port change gdb connection port\n" "-d item1,... output log to %s (use -d ? for a list of log items)\n" "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" " translation (t=none or lba) (usually qemu can guess them)\n" "-L path set the directory for the BIOS and VGA BIOS\n" #ifdef USE_KQEMU "-no-kqemu disable KQEMU kernel module usage\n" #endif #ifdef USE_CODE_COPY "-no-code-copy disable code copy acceleration\n" #endif #ifdef TARGET_I386 "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n" " (default is CL-GD5446 PCI VGA)\n" #endif "-loadvm file start right away with a saved state (loadvm in monitor)\n" "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" "ctrl-alt-n switch to virtual console 'n'\n" "ctrl-alt toggle mouse and keyboard grab\n" "\n" "When using -nographic, press 'ctrl-a h' to get some help.\n" , #ifdef CONFIG_SOFTMMU "qemu", #else "qemu-fast", #endif DEFAULT_RAM_SIZE, #ifndef _WIN32 DEFAULT_NETWORK_SCRIPT, #endif DEFAULT_GDBSTUB_PORT, "/tmp/qemu.log"); #ifndef CONFIG_SOFTMMU printf("\n" "NOTE: this version of QEMU is faster but it needs slightly patched OSes to\n" "work. Please use the 'qemu' executable to have a more accurate (but slower)\n" "PC emulation.\n"); #endif exit(1); } #define HAS_ARG 0x0001 enum { QEMU_OPTION_h, QEMU_OPTION_M, QEMU_OPTION_fda, QEMU_OPTION_fdb, QEMU_OPTION_hda, QEMU_OPTION_hdb, QEMU_OPTION_hdc, QEMU_OPTION_hdd, QEMU_OPTION_cdrom, QEMU_OPTION_boot, QEMU_OPTION_snapshot, QEMU_OPTION_m, QEMU_OPTION_nographic, #ifdef HAS_AUDIO QEMU_OPTION_enable_audio, QEMU_OPTION_audio_help, QEMU_OPTION_soundhw, #endif QEMU_OPTION_net, QEMU_OPTION_tftp, QEMU_OPTION_smb, QEMU_OPTION_redir, QEMU_OPTION_kernel, QEMU_OPTION_append, QEMU_OPTION_initrd, QEMU_OPTION_S, QEMU_OPTION_s, QEMU_OPTION_p, QEMU_OPTION_d, QEMU_OPTION_hdachs, QEMU_OPTION_L, QEMU_OPTION_no_code_copy, QEMU_OPTION_k, QEMU_OPTION_localtime, QEMU_OPTION_cirrusvga, QEMU_OPTION_g, QEMU_OPTION_std_vga, QEMU_OPTION_monitor, QEMU_OPTION_serial, QEMU_OPTION_parallel, QEMU_OPTION_loadvm, QEMU_OPTION_full_screen, QEMU_OPTION_pidfile, QEMU_OPTION_no_kqemu, QEMU_OPTION_win2k_hack, QEMU_OPTION_usb, QEMU_OPTION_usbdevice, QEMU_OPTION_smp, }; typedef struct QEMUOption { const char *name; int flags; int index; } QEMUOption; const QEMUOption qemu_options[] = { { "h", 0, QEMU_OPTION_h }, { "M", HAS_ARG, QEMU_OPTION_M }, { "fda", HAS_ARG, QEMU_OPTION_fda }, { "fdb", HAS_ARG, QEMU_OPTION_fdb }, { "hda", HAS_ARG, QEMU_OPTION_hda }, { "hdb", HAS_ARG, QEMU_OPTION_hdb }, { "hdc", HAS_ARG, QEMU_OPTION_hdc }, { "hdd", HAS_ARG, QEMU_OPTION_hdd }, { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, { "boot", HAS_ARG, QEMU_OPTION_boot }, { "snapshot", 0, QEMU_OPTION_snapshot }, { "m", HAS_ARG, QEMU_OPTION_m }, { "nographic", 0, QEMU_OPTION_nographic }, { "k", HAS_ARG, QEMU_OPTION_k }, #ifdef HAS_AUDIO { "enable-audio", 0, QEMU_OPTION_enable_audio }, { "audio-help", 0, QEMU_OPTION_audio_help }, { "soundhw", HAS_ARG, QEMU_OPTION_soundhw }, #endif { "net", HAS_ARG, QEMU_OPTION_net}, #ifdef CONFIG_SLIRP { "tftp", HAS_ARG, QEMU_OPTION_tftp }, #ifndef _WIN32 { "smb", HAS_ARG, QEMU_OPTION_smb }, #endif { "redir", HAS_ARG, QEMU_OPTION_redir }, #endif { "kernel", HAS_ARG, QEMU_OPTION_kernel }, { "append", HAS_ARG, QEMU_OPTION_append }, { "initrd", HAS_ARG, QEMU_OPTION_initrd }, { "S", 0, QEMU_OPTION_S }, { "s", 0, QEMU_OPTION_s }, { "p", HAS_ARG, QEMU_OPTION_p }, { "d", HAS_ARG, QEMU_OPTION_d }, { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, { "L", HAS_ARG, QEMU_OPTION_L }, { "no-code-copy", 0, QEMU_OPTION_no_code_copy }, #ifdef USE_KQEMU { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, #endif #if defined(TARGET_PPC) || defined(TARGET_SPARC) { "g", 1, QEMU_OPTION_g }, #endif { "localtime", 0, QEMU_OPTION_localtime }, { "std-vga", 0, QEMU_OPTION_std_vga }, { "monitor", 1, QEMU_OPTION_monitor }, { "serial", 1, QEMU_OPTION_serial }, { "parallel", 1, QEMU_OPTION_parallel }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, { "full-screen", 0, QEMU_OPTION_full_screen }, { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, { "win2k-hack", 0, QEMU_OPTION_win2k_hack }, { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, { "smp", HAS_ARG, QEMU_OPTION_smp }, /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, { NULL }, }; #if defined (TARGET_I386) && defined(USE_CODE_COPY) /* this stack is only used during signal handling */ #define SIGNAL_STACK_SIZE 32768 static uint8_t *signal_stack; #endif /* password input */ static BlockDriverState *get_bdrv(int index) { BlockDriverState *bs; if (index < 4) { bs = bs_table[index]; } else if (index < 6) { bs = fd_table[index - 4]; } else { bs = NULL; } return bs; } static void read_passwords(void) { BlockDriverState *bs; int i, j; char password[256]; for(i = 0; i < 6; i++) { bs = get_bdrv(i); if (bs && bdrv_is_encrypted(bs)) { term_printf("%s is encrypted.\n", bdrv_get_device_name(bs)); for(j = 0; j < 3; j++) { monitor_readline("Password: ", 1, password, sizeof(password)); if (bdrv_set_key(bs, password) == 0) break; term_printf("invalid password\n"); } } } } /* XXX: currently we cannot use simultaneously different CPUs */ void register_machines(void) { #if defined(TARGET_I386) qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); #elif defined(TARGET_PPC) qemu_register_machine(&heathrow_machine); qemu_register_machine(&core99_machine); qemu_register_machine(&prep_machine); #elif defined(TARGET_MIPS) qemu_register_machine(&mips_machine); #elif defined(TARGET_SPARC) #ifdef TARGET_SPARC64 qemu_register_machine(&sun4u_machine); #else qemu_register_machine(&sun4m_machine); #endif #elif defined(TARGET_ARM) qemu_register_machine(&integratorcp_machine); #else #error unsupported CPU #endif } #ifdef HAS_AUDIO static void select_soundhw (const char *optarg) { if (*optarg == '?') { show_valid_cards: printf ("Valid sound card names (comma separated):\n"); printf ("sb16 Creative Sound Blaster 16\n"); #ifdef CONFIG_ADLIB #ifdef HAS_YMF262 printf ("adlib Yamaha YMF262 (OPL3)\n"); #else printf ("adlib Yamaha YM3812 (OPL2)\n"); #endif #endif #ifdef CONFIG_GUS printf ("gus Gravis Ultrasound GF1\n"); #endif printf ("es1370 ENSONIQ AudioPCI ES1370\n"); exit (*optarg != '?'); } else { struct { char *name; int *enabledp; } soundhw_tab[] = { { "sb16", &sb16_enabled }, #ifdef CONFIG_ADLIB { "adlib", &adlib_enabled }, #endif #ifdef CONFIG_GUS { "gus", &gus_enabled }, #endif { "es1370", &es1370_enabled }, }; size_t tablen, l, i; const char *p; char *e; int bad_card = 0; p = optarg; tablen = sizeof (soundhw_tab) / sizeof (soundhw_tab[0]); while (*p) { e = strchr (p, ','); l = !e ? strlen (p) : (size_t) (e - p); for (i = 0; i < tablen; ++i) { if (!strncmp (soundhw_tab[i].name, p, l)) { audio_enabled = 1; *soundhw_tab[i].enabledp = 1; break; } } if (i == tablen) { if (l > 80) { fprintf (stderr, "Unknown sound card name (too big to show)\n"); } else { fprintf (stderr, "Unknown sound card name `%.*s'\n", (int) l, p); } bad_card = 1; } p += l + (e != NULL); } if (bad_card) goto show_valid_cards; } } #endif #define MAX_NET_CLIENTS 32 int main(int argc, char **argv) { #ifdef CONFIG_GDBSTUB int use_gdbstub, gdbstub_port; #endif int i, cdrom_index; int snapshot, linux_boot; const char *initrd_filename; const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; const char *kernel_filename, *kernel_cmdline; DisplayState *ds = &display_state; int cyls, heads, secs, translation; int start_emulation = 1; char net_clients[MAX_NET_CLIENTS][256]; int nb_net_clients; int optind; const char *r, *optarg; CharDriverState *monitor_hd; char monitor_device[128]; char serial_devices[MAX_SERIAL_PORTS][128]; int serial_device_index; char parallel_devices[MAX_PARALLEL_PORTS][128]; int parallel_device_index; const char *loadvm = NULL; QEMUMachine *machine; char usb_devices[MAX_VM_USB_PORTS][128]; int usb_devices_index; LIST_INIT (&vm_change_state_head); #if !defined(CONFIG_SOFTMMU) /* we never want that malloc() uses mmap() */ mallopt(M_MMAP_THRESHOLD, 4096 * 1024); #endif register_machines(); machine = first_machine; initrd_filename = NULL; for(i = 0; i < MAX_FD; i++) fd_filename[i] = NULL; for(i = 0; i < MAX_DISKS; i++) hd_filename[i] = NULL; ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; vga_ram_size = VGA_RAM_SIZE; bios_size = BIOS_SIZE; #ifdef CONFIG_GDBSTUB use_gdbstub = 0; gdbstub_port = DEFAULT_GDBSTUB_PORT; #endif snapshot = 0; nographic = 0; kernel_filename = NULL; kernel_cmdline = ""; #ifdef TARGET_PPC cdrom_index = 1; #else cdrom_index = 2; #endif cyls = heads = secs = 0; translation = BIOS_ATA_TRANSLATION_AUTO; pstrcpy(monitor_device, sizeof(monitor_device), "vc"); pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "vc"); for(i = 1; i < MAX_SERIAL_PORTS; i++) serial_devices[i][0] = '\0'; serial_device_index = 0; pstrcpy(parallel_devices[0], sizeof(parallel_devices[0]), "vc"); for(i = 1; i < MAX_PARALLEL_PORTS; i++) parallel_devices[i][0] = '\0'; parallel_device_index = 0; usb_devices_index = 0; nb_net_clients = 0; nb_nics = 0; /* default mac address of the first network interface */ optind = 1; for(;;) { if (optind >= argc) break; r = argv[optind]; if (r[0] != '-') { hd_filename[0] = argv[optind++]; } else { const QEMUOption *popt; optind++; popt = qemu_options; for(;;) { if (!popt->name) { fprintf(stderr, "%s: invalid option -- '%s'\n", argv[0], r); exit(1); } if (!strcmp(popt->name, r + 1)) break; popt++; } if (popt->flags & HAS_ARG) { if (optind >= argc) { fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], r); exit(1); } optarg = argv[optind++]; } else { optarg = NULL; } switch(popt->index) { case QEMU_OPTION_M: machine = find_machine(optarg); if (!machine) { QEMUMachine *m; printf("Supported machines are:\n"); for(m = first_machine; m != NULL; m = m->next) { printf("%-10s %s%s\n", m->name, m->desc, m == first_machine ? " (default)" : ""); } exit(1); } break; case QEMU_OPTION_initrd: initrd_filename = optarg; break; case QEMU_OPTION_hda: case QEMU_OPTION_hdb: case QEMU_OPTION_hdc: case QEMU_OPTION_hdd: { int hd_index; hd_index = popt->index - QEMU_OPTION_hda; hd_filename[hd_index] = optarg; if (hd_index == cdrom_index) cdrom_index = -1; } break; case QEMU_OPTION_snapshot: snapshot = 1; break; case QEMU_OPTION_hdachs: { const char *p; p = optarg; cyls = strtol(p, (char **)&p, 0); if (cyls < 1 || cyls > 16383) goto chs_fail; if (*p != ',') goto chs_fail; p++; heads = strtol(p, (char **)&p, 0); if (heads < 1 || heads > 16) goto chs_fail; if (*p != ',') goto chs_fail; p++; secs = strtol(p, (char **)&p, 0); if (secs < 1 || secs > 63) goto chs_fail; if (*p == ',') { p++; if (!strcmp(p, "none")) translation = BIOS_ATA_TRANSLATION_NONE; else if (!strcmp(p, "lba")) translation = BIOS_ATA_TRANSLATION_LBA; else if (!strcmp(p, "auto")) translation = BIOS_ATA_TRANSLATION_AUTO; else goto chs_fail; } else if (*p != '\0') { chs_fail: fprintf(stderr, "qemu: invalid physical CHS format\n"); exit(1); } } break; case QEMU_OPTION_nographic: pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio"); nographic = 1; break; case QEMU_OPTION_kernel: kernel_filename = optarg; break; case QEMU_OPTION_append: kernel_cmdline = optarg; break; case QEMU_OPTION_cdrom: if (cdrom_index >= 0) { hd_filename[cdrom_index] = optarg; } break; case QEMU_OPTION_boot: boot_device = optarg[0]; if (boot_device != 'a' && #ifdef TARGET_SPARC // Network boot boot_device != 'n' && #endif boot_device != 'c' && boot_device != 'd') { fprintf(stderr, "qemu: invalid boot device '%c'\n", boot_device); exit(1); } break; case QEMU_OPTION_fda: fd_filename[0] = optarg; break; case QEMU_OPTION_fdb: fd_filename[1] = optarg; break; case QEMU_OPTION_no_code_copy: code_copy_enabled = 0; break; case QEMU_OPTION_net: if (nb_net_clients >= MAX_NET_CLIENTS) { fprintf(stderr, "qemu: too many network clients\n"); exit(1); } pstrcpy(net_clients[nb_net_clients], sizeof(net_clients[0]), optarg); nb_net_clients++; break; #ifdef CONFIG_SLIRP case QEMU_OPTION_tftp: tftp_prefix = optarg; break; #ifndef _WIN32 case QEMU_OPTION_smb: net_slirp_smb(optarg); break; #endif case QEMU_OPTION_redir: net_slirp_redir(optarg); break; #endif #ifdef HAS_AUDIO case QEMU_OPTION_enable_audio: audio_enabled = 1; sb16_enabled = 1; adlib_enabled = 1; gus_enabled = 1; es1370_enabled = 1; break; case QEMU_OPTION_audio_help: AUD_help (); exit (0); break; case QEMU_OPTION_soundhw: select_soundhw (optarg); break; #endif case QEMU_OPTION_h: help(); break; case QEMU_OPTION_m: ram_size = atoi(optarg) * 1024 * 1024; if (ram_size <= 0) help(); if (ram_size > PHYS_RAM_MAX_SIZE) { fprintf(stderr, "qemu: at most %d MB RAM can be simulated\n", PHYS_RAM_MAX_SIZE / (1024 * 1024)); exit(1); } break; case QEMU_OPTION_d: { int mask; CPULogItem *item; mask = cpu_str_to_log_mask(optarg); if (!mask) { printf("Log items (comma separated):\n"); for(item = cpu_log_items; item->mask != 0; item++) { printf("%-10s %s\n", item->name, item->help); } exit(1); } cpu_set_log(mask); } break; #ifdef CONFIG_GDBSTUB case QEMU_OPTION_s: use_gdbstub = 1; break; case QEMU_OPTION_p: gdbstub_port = atoi(optarg); break; #endif case QEMU_OPTION_L: bios_dir = optarg; break; case QEMU_OPTION_S: start_emulation = 0; break; case QEMU_OPTION_k: keyboard_layout = optarg; break; case QEMU_OPTION_localtime: rtc_utc = 0; break; case QEMU_OPTION_cirrusvga: cirrus_vga_enabled = 1; break; case QEMU_OPTION_std_vga: cirrus_vga_enabled = 0; break; case QEMU_OPTION_g: { const char *p; int w, h, depth; p = optarg; w = strtol(p, (char **)&p, 10); if (w <= 0) { graphic_error: fprintf(stderr, "qemu: invalid resolution or depth\n"); exit(1); } if (*p != 'x') goto graphic_error; p++; h = strtol(p, (char **)&p, 10); if (h <= 0) goto graphic_error; if (*p == 'x') { p++; depth = strtol(p, (char **)&p, 10); if (depth != 8 && depth != 15 && depth != 16 && depth != 24 && depth != 32) goto graphic_error; } else if (*p == '\0') { depth = graphic_depth; } else { goto graphic_error; } graphic_width = w; graphic_height = h; graphic_depth = depth; } break; case QEMU_OPTION_monitor: pstrcpy(monitor_device, sizeof(monitor_device), optarg); break; case QEMU_OPTION_serial: if (serial_device_index >= MAX_SERIAL_PORTS) { fprintf(stderr, "qemu: too many serial ports\n"); exit(1); } pstrcpy(serial_devices[serial_device_index], sizeof(serial_devices[0]), optarg); serial_device_index++; break; case QEMU_OPTION_parallel: if (parallel_device_index >= MAX_PARALLEL_PORTS) { fprintf(stderr, "qemu: too many parallel ports\n"); exit(1); } pstrcpy(parallel_devices[parallel_device_index], sizeof(parallel_devices[0]), optarg); parallel_device_index++; break; case QEMU_OPTION_loadvm: loadvm = optarg; break; case QEMU_OPTION_full_screen: full_screen = 1; break; case QEMU_OPTION_pidfile: create_pidfile(optarg); break; #ifdef TARGET_I386 case QEMU_OPTION_win2k_hack: win2k_install_hack = 1; break; #endif #ifdef USE_KQEMU case QEMU_OPTION_no_kqemu: kqemu_allowed = 0; break; #endif case QEMU_OPTION_usb: usb_enabled = 1; break; case QEMU_OPTION_usbdevice: usb_enabled = 1; if (usb_devices_index >= MAX_VM_USB_PORTS) { fprintf(stderr, "Too many USB devices\n"); exit(1); } pstrcpy(usb_devices[usb_devices_index], sizeof(usb_devices[usb_devices_index]), optarg); usb_devices_index++; break; case QEMU_OPTION_smp: smp_cpus = atoi(optarg); if (smp_cpus < 1 || smp_cpus > MAX_CPUS) { fprintf(stderr, "Invalid number of CPUs\n"); exit(1); } break; } } } linux_boot = (kernel_filename != NULL); if (!linux_boot && hd_filename[0] == '\0' && (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') && fd_filename[0] == '\0') help(); /* boot to cd by default if no hard disk */ if (hd_filename[0] == '\0' && boot_device == 'c') { if (fd_filename[0] != '\0') boot_device = 'a'; else boot_device = 'd'; } #if !defined(CONFIG_SOFTMMU) /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ { static uint8_t stdout_buf[4096]; setvbuf(stdout, stdout_buf, _IOLBF, sizeof(stdout_buf)); } #else setvbuf(stdout, NULL, _IOLBF, 0); #endif /* init network clients */ if (nb_net_clients == 0) { /* if no clients, we use a default config */ pstrcpy(net_clients[0], sizeof(net_clients[0]), "nic"); pstrcpy(net_clients[1], sizeof(net_clients[0]), "user"); nb_net_clients = 2; } for(i = 0;i < nb_net_clients; i++) { if (net_client_init(net_clients[i]) < 0) exit(1); } /* init the memory */ phys_ram_size = ram_size + vga_ram_size + bios_size; #ifdef CONFIG_SOFTMMU phys_ram_base = qemu_vmalloc(phys_ram_size); if (!phys_ram_base) { fprintf(stderr, "Could not allocate physical memory\n"); exit(1); } #else /* as we must map the same page at several addresses, we must use a fd */ { const char *tmpdir; tmpdir = getenv("QEMU_TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir); if (mkstemp(phys_ram_file) < 0) { fprintf(stderr, "Could not create temporary memory file '%s'\n", phys_ram_file); exit(1); } phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600); if (phys_ram_fd < 0) { fprintf(stderr, "Could not open temporary memory file '%s'\n", phys_ram_file); exit(1); } ftruncate(phys_ram_fd, phys_ram_size); unlink(phys_ram_file); phys_ram_base = mmap(get_mmap_addr(phys_ram_size), phys_ram_size, PROT_WRITE | PROT_READ, MAP_SHARED | MAP_FIXED, phys_ram_fd, 0); if (phys_ram_base == MAP_FAILED) { fprintf(stderr, "Could not map physical memory\n"); exit(1); } } #endif /* we always create the cdrom drive, even if no disk is there */ bdrv_init(); if (cdrom_index >= 0) { bs_table[cdrom_index] = bdrv_new("cdrom"); bdrv_set_type_hint(bs_table[cdrom_index], BDRV_TYPE_CDROM); } /* open the virtual block devices */ for(i = 0; i < MAX_DISKS; i++) { if (hd_filename[i]) { if (!bs_table[i]) { char buf[64]; snprintf(buf, sizeof(buf), "hd%c", i + 'a'); bs_table[i] = bdrv_new(buf); } if (bdrv_open(bs_table[i], hd_filename[i], snapshot) < 0) { fprintf(stderr, "qemu: could not open hard disk image '%s'\n", hd_filename[i]); exit(1); } if (i == 0 && cyls != 0) { bdrv_set_geometry_hint(bs_table[i], cyls, heads, secs); bdrv_set_translation_hint(bs_table[i], translation); } } } /* we always create at least one floppy disk */ fd_table[0] = bdrv_new("fda"); bdrv_set_type_hint(fd_table[0], BDRV_TYPE_FLOPPY); for(i = 0; i < MAX_FD; i++) { if (fd_filename[i]) { if (!fd_table[i]) { char buf[64]; snprintf(buf, sizeof(buf), "fd%c", i + 'a'); fd_table[i] = bdrv_new(buf); bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY); } if (fd_filename[i] != '\0') { if (bdrv_open(fd_table[i], fd_filename[i], snapshot) < 0) { fprintf(stderr, "qemu: could not open floppy disk image '%s'\n", fd_filename[i]); exit(1); } } } } /* init USB devices */ if (usb_enabled) { vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS); for(i = 0; i < usb_devices_index; i++) { if (usb_device_add(usb_devices[i]) < 0) { fprintf(stderr, "Warning: could not add USB device %s\n", usb_devices[i]); } } } register_savevm("timer", 0, 1, timer_save, timer_load, NULL); register_savevm("ram", 0, 1, ram_save, ram_load, NULL); init_ioports(); cpu_calibrate_ticks(); /* terminal init */ if (nographic) { dumb_display_init(ds); } else { #if defined(CONFIG_SDL) sdl_display_init(ds, full_screen); #elif defined(CONFIG_COCOA) cocoa_display_init(ds, full_screen); #else dumb_display_init(ds); #endif } vga_console = graphic_console_init(ds); monitor_hd = qemu_chr_open(monitor_device); if (!monitor_hd) { fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); exit(1); } monitor_init(monitor_hd, !nographic); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_devices[i][0] != '\0') { serial_hds[i] = qemu_chr_open(serial_devices[i]); if (!serial_hds[i]) { fprintf(stderr, "qemu: could not open serial device '%s'\n", serial_devices[i]); exit(1); } if (!strcmp(serial_devices[i], "vc")) qemu_chr_printf(serial_hds[i], "serial%d console\n", i); } } for(i = 0; i < MAX_PARALLEL_PORTS; i++) { if (parallel_devices[i][0] != '\0') { parallel_hds[i] = qemu_chr_open(parallel_devices[i]); if (!parallel_hds[i]) { fprintf(stderr, "qemu: could not open parallel device '%s'\n", parallel_devices[i]); exit(1); } if (!strcmp(parallel_devices[i], "vc")) qemu_chr_printf(parallel_hds[i], "parallel%d console\n", i); } } /* setup cpu signal handlers for MMU / self modifying code handling */ #if !defined(CONFIG_SOFTMMU) #if defined (TARGET_I386) && defined(USE_CODE_COPY) { stack_t stk; signal_stack = memalign(16, SIGNAL_STACK_SIZE); stk.ss_sp = signal_stack; stk.ss_size = SIGNAL_STACK_SIZE; stk.ss_flags = 0; if (sigaltstack(&stk, NULL) < 0) { perror("sigaltstack"); exit(1); } } #endif { struct sigaction act; sigfillset(&act.sa_mask); act.sa_flags = SA_SIGINFO; #if defined (TARGET_I386) && defined(USE_CODE_COPY) act.sa_flags |= SA_ONSTACK; #endif act.sa_sigaction = host_segv_handler; sigaction(SIGSEGV, &act, NULL); sigaction(SIGBUS, &act, NULL); #if defined (TARGET_I386) && defined(USE_CODE_COPY) sigaction(SIGFPE, &act, NULL); #endif } #endif #ifndef _WIN32 { struct sigaction act; sigfillset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); } #endif init_timers(); machine->init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, initrd_filename); gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); #ifdef CONFIG_GDBSTUB if (use_gdbstub) { if (gdbserver_start(gdbstub_port) < 0) { fprintf(stderr, "Could not open gdbserver socket on port %d\n", gdbstub_port); exit(1); } else { printf("Waiting gdb connection on port %d\n", gdbstub_port); } } else #endif if (loadvm) qemu_loadvm(loadvm); { /* XXX: simplify init */ read_passwords(); if (start_emulation) { vm_start(); } } main_loop(); quit_timers(); return 0; }