X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=vl.c;h=e828d316ad05bb696990f5946506ca254dc570de;hb=7ef4da1c3a753888e2678388150f1b846b025168;hp=725e954d3e6aba8f807129bd093cfc1a63687ebd;hpb=7c9d8e07e188597e570a311efc46cb1ab013e5e7;p=qemu diff --git a/vl.c b/vl.c index 725e954..e828d31 100644 --- a/vl.c +++ b/vl.c @@ -47,6 +47,7 @@ #include #endif #else +#ifndef __sun__ #include #include #include @@ -55,6 +56,7 @@ #include #endif #endif +#endif #if defined(CONFIG_SLIRP) #include "libslirp.h" @@ -68,6 +70,8 @@ #define memalign(align, size) malloc(size) #endif +#include "qemu_socket.h" + #ifdef CONFIG_SDL #ifdef __APPLE__ #include @@ -83,8 +87,6 @@ #include "exec-all.h" -//#define DO_TB_FLUSH - #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" //#define DEBUG_UNUSED_IOPORT @@ -109,8 +111,6 @@ const char *bios_dir = CONFIG_QEMU_SHAREDIR; char phys_ram_file[1024]; -CPUState *global_env; -CPUState *cpu_single_env; void *ioport_opaque[MAX_IOPORTS]; IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; @@ -128,13 +128,6 @@ 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 @@ -146,7 +139,6 @@ 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 @@ -156,6 +148,16 @@ int usb_enabled = 0; USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; USBDevice *vm_usb_hub; static VLANState *first_vlan; +int smp_cpus = 1; +int vnc_display = -1; +#if defined(TARGET_SPARC) +#define MAX_CPUS 16 +#elif defined(TARGET_I386) +#define MAX_CPUS 255 +#else +#define MAX_CPUS 1 +#endif +int acpi_enabled = 1; /***********************************************************/ /* x86 ISA bus support */ @@ -334,35 +336,6 @@ int strstart(const char *str, const char *val, const char **ptr) 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 @@ -370,6 +343,10 @@ void cpu_outb(CPUState *env, int addr, int val) fprintf(logfile, "outb: %04x %02x\n", addr, val); #endif ioport_write_table[0][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif } void cpu_outw(CPUState *env, int addr, int val) @@ -379,6 +356,10 @@ void cpu_outw(CPUState *env, int addr, int val) fprintf(logfile, "outw: %04x %04x\n", addr, val); #endif ioport_write_table[1][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif } void cpu_outl(CPUState *env, int addr, int val) @@ -388,6 +369,10 @@ void cpu_outl(CPUState *env, int addr, int val) fprintf(logfile, "outl: %04x %08x\n", addr, val); #endif ioport_write_table[2][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif } int cpu_inb(CPUState *env, int addr) @@ -398,6 +383,10 @@ int cpu_inb(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inb : %04x %02x\n", addr, val); #endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif return val; } @@ -409,6 +398,10 @@ int cpu_inw(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inw : %04x %04x\n", addr, val); #endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif return val; } @@ -420,6 +413,10 @@ int cpu_inl(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inl : %04x %08x\n", addr, val); #endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif return val; } @@ -427,16 +424,20 @@ int cpu_inl(CPUState *env, int addr) 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(global_env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); #else - cpu_dump_state(global_env, stderr, fprintf, 0); + cpu_dump_state(env, stderr, fprintf, 0); #endif + } va_end(ap); abort(); } @@ -448,6 +449,7 @@ 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; +static int qemu_put_mouse_event_absolute; void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { @@ -455,10 +457,11 @@ void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) qemu_put_kbd_event = func; } -void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque) +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute) { qemu_put_mouse_event_opaque = opaque; qemu_put_mouse_event = func; + qemu_put_mouse_event_absolute = absolute; } void kbd_put_keycode(int keycode) @@ -476,6 +479,11 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) } } +int kbd_mouse_is_absolute(void) +{ + return qemu_put_mouse_event_absolute; +} + /***********************************************************/ /* timers */ @@ -511,9 +519,15 @@ int64_t cpu_get_real_ticks(void) int64_t cpu_get_real_ticks(void) { +#ifdef _WIN32 + LARGE_INTEGER ti; + QueryPerformanceCounter(&ti); + return ti.QuadPart; +#else int64_t val; asm volatile ("rdtsc" : "=A" (val)); return val; +#endif } #elif defined(__x86_64__) @@ -551,6 +565,7 @@ int64_t cpu_get_real_ticks(void) #error unsupported CPU #endif +static int64_t cpu_ticks_prev; static int64_t cpu_ticks_offset; static int cpu_ticks_enabled; @@ -559,7 +574,15 @@ 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; + int64_t ticks; + ticks = cpu_get_real_ticks(); + if (cpu_ticks_prev > ticks) { + /* Note: non increasing ticks may happen if the host uses + software suspend */ + cpu_ticks_offset += cpu_ticks_prev - ticks; + } + cpu_ticks_prev = ticks; + return ticks + cpu_ticks_offset; } } @@ -582,17 +605,26 @@ void cpu_disable_ticks(void) } } -static int64_t get_clock(void) -{ #ifdef _WIN32 - struct _timeb tb; - _ftime(&tb); - return ((int64_t)tb.time * 1000 + (int64_t)tb.millitm) * 1000; +void cpu_calibrate_ticks(void) +{ + LARGE_INTEGER freq; + int ret; + + ret = QueryPerformanceFrequency(&freq); + if (ret == 0) { + fprintf(stderr, "Could not calibrate ticks\n"); + exit(1); + } + ticks_per_sec = freq.QuadPart; +} + #else +static int64_t get_clock(void) +{ struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000LL + tv.tv_usec; -#endif } void cpu_calibrate_ticks(void) @@ -601,15 +633,12 @@ void cpu_calibrate_ticks(void) 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; } +#endif /* !_WIN32 */ /* compute with 96 bit intermediate result: (a*b)/c */ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) @@ -657,6 +686,8 @@ QEMUClock *vm_clock; static QEMUTimer *active_timers[2]; #ifdef _WIN32 static MMRESULT timerID; +static HANDLE host_alarm = NULL; +static unsigned int period = 1; #else /* frequency of the times() clock tick */ static int timer_freq; @@ -879,13 +910,19 @@ static void host_alarm_handler(int host_signum) qemu_get_clock(vm_clock)) || qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], qemu_get_clock(rt_clock))) { - /* stop the cpu because a timer occured */ - cpu_interrupt(global_env, CPU_INTERRUPT_EXIT); +#ifdef _WIN32 + SetEvent(host_alarm); +#endif + 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 (global_env->kqemu_enabled) { - kqemu_cpu_interrupt(global_env); - } + if (env->kqemu_enabled) { + kqemu_cpu_interrupt(env); + } #endif + } } } @@ -936,8 +973,15 @@ static void init_timers(void) #ifdef _WIN32 { int count=0; + TIMECAPS tc; + + ZeroMemory(&tc, sizeof(TIMECAPS)); + timeGetDevCaps(&tc, sizeof(TIMECAPS)); + if (period < tc.wPeriodMin) + period = tc.wPeriodMin; + timeBeginPeriod(period); timerID = timeSetEvent(1, // interval (ms) - 0, // resolution + period, // resolution host_alarm_handler, // function (DWORD)&count, // user parameter TIME_PERIODIC | TIME_CALLBACK_FUNCTION); @@ -945,6 +989,12 @@ static void init_timers(void) perror("failed timer alarm"); exit(1); } + host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!host_alarm) { + perror("failed CreateEvent"); + exit(1); + } + ResetEvent(host_alarm); } pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000; #else @@ -974,7 +1024,11 @@ static void init_timers(void) getitimer(ITIMER_REAL, &itv); #if defined(__linux__) - if (itv.it_interval.tv_usec > 1000) { + /* XXX: force /dev/rtc usage because even 2.6 kernels may not + have timers with 1 ms resolution. The correct solution will + be to use the POSIX real time timers available in recent + 2.6 kernels */ + if (itv.it_interval.tv_usec > 1000 || 1) { /* try to use /dev/rtc to have a faster timer */ if (start_rtc_timer() < 0) goto use_itimer; @@ -1004,6 +1058,11 @@ void quit_timers(void) { #ifdef _WIN32 timeKillEvent(timerID); + timeEndPeriod(period); + if (host_alarm) { + CloseHandle(host_alarm); + host_alarm = NULL; + } #endif } @@ -1073,20 +1132,58 @@ CharDriverState *qemu_chr_open_null(void) return chr; } -#ifndef _WIN32 +#ifdef _WIN32 -typedef struct { - int fd_in, fd_out; - IOCanRWHandler *fd_can_read; - IOReadHandler *fd_read; - void *fd_opaque; - int max_size; -} FDCharDriver; +static void socket_cleanup(void) +{ + WSACleanup(); +} -#define STDIO_MAX_CLIENTS 2 +static int socket_init(void) +{ + WSADATA Data; + int ret, err; -static int stdio_nb_clients; -static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; + ret = WSAStartup(MAKEWORD(2,2), &Data); + if (ret != 0) { + err = WSAGetLastError(); + fprintf(stderr, "WSAStartup: %d\n", err); + return -1; + } + atexit(socket_cleanup); + return 0; +} + +static int send_all(int fd, const uint8_t *buf, int len1) +{ + int ret, len; + + len = len1; + while (len > 0) { + ret = send(fd, buf, len, 0); + if (ret < 0) { + int errno; + errno = WSAGetLastError(); + if (errno != WSAEWOULDBLOCK) { + return -1; + } + } else if (ret == 0) { + break; + } else { + buf += ret; + len -= ret; + } + } + return len1 - len; +} + +void socket_set_nonblock(int fd) +{ + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +} + +#else static int unix_write(int fd, const uint8_t *buf, int len1) { @@ -1108,6 +1205,32 @@ static int unix_write(int fd, const uint8_t *buf, int len1) return len1 - len; } +static inline int send_all(int fd, const uint8_t *buf, int len1) +{ + return unix_write(fd, buf, len1); +} + +void socket_set_nonblock(int fd) +{ + fcntl(fd, F_SETFL, O_NONBLOCK); +} +#endif /* !_WIN32 */ + +#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 fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { FDCharDriver *s = chr->opaque; @@ -1185,7 +1308,7 @@ 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); + fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666); if (fd_out < 0) return NULL; return qemu_chr_open_fd(-1, fd_out); @@ -1387,6 +1510,7 @@ CharDriverState *qemu_chr_open_stdio(void) #if defined(__linux__) CharDriverState *qemu_chr_open_pty(void) { + struct termios tty; char slave_name[1024]; int master_fd, slave_fd; @@ -1394,6 +1518,14 @@ CharDriverState *qemu_chr_open_pty(void) 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); } @@ -1602,20 +1734,393 @@ CharDriverState *qemu_chr_open_pty(void) #endif /* !defined(_WIN32) */ +#ifdef _WIN32 +typedef struct { + IOCanRWHandler *fd_can_read; + IOReadHandler *fd_read; + void *win_opaque; + int max_size; + HANDLE hcom, hrecv, hsend; + OVERLAPPED orecv, osend; + BOOL fpipe; + DWORD len; +} WinCharState; + +#define NSENDBUF 2048 +#define NRECVBUF 2048 +#define MAXCONNECT 1 +#define NTIMEOUT 5000 + +static int win_chr_poll(void *opaque); +static int win_chr_pipe_poll(void *opaque); + +static void win_chr_close2(WinCharState *s) +{ + if (s->hsend) { + CloseHandle(s->hsend); + s->hsend = NULL; + } + if (s->hrecv) { + CloseHandle(s->hrecv); + s->hrecv = NULL; + } + if (s->hcom) { + CloseHandle(s->hcom); + s->hcom = NULL; + } + if (s->fpipe) + qemu_del_polling_cb(win_chr_pipe_poll, s); + else + qemu_del_polling_cb(win_chr_poll, s); +} + +static void win_chr_close(CharDriverState *chr) +{ + WinCharState *s = chr->opaque; + win_chr_close2(s); +} + +static int win_chr_init(WinCharState *s, const char *filename) +{ + COMMCONFIG comcfg; + COMMTIMEOUTS cto = { 0, 0, 0, 0, 0}; + COMSTAT comstat; + DWORD size; + DWORD err; + + s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hsend) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hrecv) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + + s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (s->hcom == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError()); + s->hcom = NULL; + goto fail; + } + + if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) { + fprintf(stderr, "Failed SetupComm\n"); + goto fail; + } + + ZeroMemory(&comcfg, sizeof(COMMCONFIG)); + size = sizeof(COMMCONFIG); + GetDefaultCommConfig(filename, &comcfg, &size); + comcfg.dcb.DCBlength = sizeof(DCB); + CommConfigDialog(filename, NULL, &comcfg); + + if (!SetCommState(s->hcom, &comcfg.dcb)) { + fprintf(stderr, "Failed SetCommState\n"); + goto fail; + } + + if (!SetCommMask(s->hcom, EV_ERR)) { + fprintf(stderr, "Failed SetCommMask\n"); + goto fail; + } + + cto.ReadIntervalTimeout = MAXDWORD; + if (!SetCommTimeouts(s->hcom, &cto)) { + fprintf(stderr, "Failed SetCommTimeouts\n"); + goto fail; + } + + if (!ClearCommError(s->hcom, &err, &comstat)) { + fprintf(stderr, "Failed ClearCommError\n"); + goto fail; + } + qemu_add_polling_cb(win_chr_poll, s); + return 0; + + fail: + win_chr_close2(s); + return -1; +} + +static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) +{ + WinCharState *s = chr->opaque; + DWORD len, ret, size, err; + + len = len1; + ZeroMemory(&s->osend, sizeof(s->osend)); + s->osend.hEvent = s->hsend; + while (len > 0) { + if (s->hsend) + ret = WriteFile(s->hcom, buf, len, &size, &s->osend); + else + ret = WriteFile(s->hcom, buf, len, &size, NULL); + if (!ret) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE); + if (ret) { + buf += size; + len -= size; + } else { + break; + } + } else { + break; + } + } else { + buf += size; + len -= size; + } + } + return len1 - len; +} + +static int win_chr_read_poll(WinCharState *s) +{ + s->max_size = s->fd_can_read(s->win_opaque); + return s->max_size; +} + +static void win_chr_readfile(WinCharState *s) +{ + int ret, err; + uint8_t buf[1024]; + DWORD size; + + ZeroMemory(&s->orecv, sizeof(s->orecv)); + s->orecv.hEvent = s->hrecv; + ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv); + if (!ret) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE); + } + } + + if (size > 0) { + s->fd_read(s->win_opaque, buf, size); + } +} + +static void win_chr_read(WinCharState *s) +{ + if (s->len > s->max_size) + s->len = s->max_size; + if (s->len == 0) + return; + + win_chr_readfile(s); +} + +static int win_chr_poll(void *opaque) +{ + WinCharState *s = opaque; + COMSTAT status; + DWORD comerr; + + ClearCommError(s->hcom, &comerr, &status); + if (status.cbInQue > 0) { + s->len = status.cbInQue; + win_chr_read_poll(s); + win_chr_read(s); + return 1; + } + return 0; +} + +static void win_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + WinCharState *s = chr->opaque; + + s->fd_can_read = fd_can_read; + s->fd_read = fd_read; + s->win_opaque = opaque; +} + +CharDriverState *qemu_chr_open_win(const char *filename) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + chr->chr_close = win_chr_close; + + if (win_chr_init(s, filename) < 0) { + free(s); + free(chr); + return NULL; + } + return chr; +} + +static int win_chr_pipe_poll(void *opaque) +{ + WinCharState *s = opaque; + DWORD size; + + PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL); + if (size > 0) { + s->len = size; + win_chr_read_poll(s); + win_chr_read(s); + return 1; + } + return 0; +} + +static int win_chr_pipe_init(WinCharState *s, const char *filename) +{ + OVERLAPPED ov; + int ret; + DWORD size; + char openname[256]; + + s->fpipe = TRUE; + + s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hsend) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hrecv) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + + snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename); + s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | + PIPE_WAIT, + MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL); + if (s->hcom == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError()); + s->hcom = NULL; + goto fail; + } + + ZeroMemory(&ov, sizeof(ov)); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + ret = ConnectNamedPipe(s->hcom, &ov); + if (ret) { + fprintf(stderr, "Failed ConnectNamedPipe\n"); + goto fail; + } + + ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE); + if (!ret) { + fprintf(stderr, "Failed GetOverlappedResult\n"); + if (ov.hEvent) { + CloseHandle(ov.hEvent); + ov.hEvent = NULL; + } + goto fail; + } + + if (ov.hEvent) { + CloseHandle(ov.hEvent); + ov.hEvent = NULL; + } + qemu_add_polling_cb(win_chr_pipe_poll, s); + return 0; + + fail: + win_chr_close2(s); + return -1; +} + + +CharDriverState *qemu_chr_open_win_pipe(const char *filename) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + chr->chr_close = win_chr_close; + + if (win_chr_pipe_init(s, filename) < 0) { + free(s); + free(chr); + return NULL; + } + return chr; +} + +CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + s->hcom = fd_out; + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + return chr; +} + +CharDriverState *qemu_chr_open_win_file_out(const char *file_out) +{ + HANDLE fd_out; + + fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd_out == INVALID_HANDLE_VALUE) + return NULL; + + return qemu_chr_open_win_file(fd_out); +} +#endif + 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 if (strstart(filename, "file:", &p)) { + } 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 -#ifndef _WIN32 - if (!strcmp(filename, "pty")) { + } else if (!strcmp(filename, "pty")) { return qemu_chr_open_pty(); } else if (!strcmp(filename, "stdio")) { return qemu_chr_open_stdio(); @@ -1629,11 +2134,28 @@ CharDriverState *qemu_chr_open(const char *filename) return qemu_chr_open_tty(filename); } else #endif +#ifdef _WIN32 + if (strstart(filename, "COM", NULL)) { + return qemu_chr_open_win(filename); + } else + if (strstart(filename, "pipe:", &p)) { + return qemu_chr_open_win_pipe(p); + } else + if (strstart(filename, "file:", &p)) { + return qemu_chr_open_win_file_out(p); + } +#endif { return NULL; } } +void qemu_chr_close(CharDriverState *chr) +{ + if (chr->chr_close) + chr->chr_close(chr); +} + /***********************************************************/ /* network device redirectors */ @@ -1718,13 +2240,9 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str) 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); @@ -1755,13 +2273,16 @@ VLANState *qemu_find_vlan(int id) } VLANClientState *qemu_new_vlan_client(VLANState *vlan, - IOReadHandler *fd_read, void *opaque) + IOReadHandler *fd_read, + IOCanRWHandler *fd_can_read, + void *opaque) { VLANClientState *vc, **pvc; vc = qemu_mallocz(sizeof(VLANClientState)); if (!vc) return NULL; vc->fd_read = fd_read; + vc->fd_can_read = fd_can_read; vc->opaque = opaque; vc->vlan = vlan; @@ -1773,6 +2294,20 @@ VLANClientState *qemu_new_vlan_client(VLANState *vlan, return vc; } +int qemu_can_send_packet(VLANClientState *vc1) +{ + VLANState *vlan = vc1->vlan; + VLANClientState *vc; + + for(vc = vlan->first_client; vc != NULL; vc = vc->next) { + if (vc != vc1) { + if (vc->fd_can_read && !vc->fd_can_read(vc->opaque)) + return 0; + } + } + return 1; +} + void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size) { VLANState *vlan = vc1->vlan; @@ -1798,7 +2333,7 @@ static VLANClientState *slirp_vc; int slirp_can_output(void) { - return 1; + return !slirp_vc || qemu_can_send_packet(slirp_vc); } void slirp_output(const uint8_t *pkt, int pkt_len) @@ -1807,6 +2342,8 @@ void slirp_output(const uint8_t *pkt, int pkt_len) printf("slirp output:\n"); hex_dump(stdout, pkt, pkt_len); #endif + if (!slirp_vc) + return; qemu_send_packet(slirp_vc, pkt, pkt_len); } @@ -1826,7 +2363,7 @@ static int net_slirp_init(VLANState *vlan) slirp_init(); } slirp_vc = qemu_new_vlan_client(vlan, - slirp_receive, NULL); + slirp_receive, NULL, NULL); snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector"); return 0; } @@ -2011,7 +2548,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan, int fd) if (!s) return NULL; s->fd = fd; - s->vc = qemu_new_vlan_client(vlan, tap_receive, s); + s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, 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; @@ -2037,6 +2574,12 @@ static int tap_open(char *ifname, int ifname_size) fcntl(fd, F_SETFL, O_NONBLOCK); return fd; } +#elif defined(__sun__) +static int tap_open(char *ifname, int ifname_size) +{ + fprintf(stderr, "warning: tap_open not yet implemented\n"); + return -1; +} #else static int tap_open(char *ifname, int ifname_size) { @@ -2095,7 +2638,7 @@ static int net_tap_init(VLANState *vlan, const char *ifname1, *parg++ = ifname; *parg++ = NULL; execv(setup_script, args); - exit(1); + _exit(1); } while (waitpid(pid, &status, 0) != pid); if (!WIFEXITED(status) || @@ -2114,6 +2657,8 @@ static int net_tap_init(VLANState *vlan, const char *ifname1, return 0; } +#endif /* !_WIN32 */ + /* network connection */ typedef struct NetSocketState { VLANClientState *vc; @@ -2122,6 +2667,7 @@ typedef struct NetSocketState { 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 { @@ -2136,60 +2682,203 @@ static void net_socket_receive(void *opaque, const uint8_t *buf, int size) uint32_t len; len = htonl(size); - unix_write(s->fd, (const uint8_t *)&len, sizeof(len)); - unix_write(s->fd, buf, size); + send_all(s->fd, (const uint8_t *)&len, sizeof(len)); + send_all(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, err; + uint8_t buf1[4096]; + const uint8_t *buf; + + size = recv(s->fd, buf1, sizeof(buf1), 0); + if (size < 0) { + err = socket_error(); + if (err != EWOULDBLOCK) + goto eoc; + } else if (size == 0) { + /* end of connection */ + eoc: + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + 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), + (int)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; + } + + val = 1; + ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&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; + } + + /* 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, + (const char *)&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, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); + goto fail; + } + + socket_set_nonblock(fd); + return fd; +fail: + if (fd>=0) close(fd); + return -1; } -static void net_socket_send(void *opaque) +static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd, + int is_connected) { - NetSocketState *s = opaque; - int l, size; - uint8_t buf1[4096]; - const uint8_t *buf; + struct sockaddr_in saddr; + int newfd; + socklen_t saddr_len; + NetSocketState *s; - 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; - } + /* 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, NULL, 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) @@ -2198,7 +2887,7 @@ static void net_socket_connect(void *opaque) qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); } -static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, +static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd, int is_connected) { NetSocketState *s; @@ -2207,7 +2896,7 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, return NULL; s->fd = fd; s->vc = qemu_new_vlan_client(vlan, - net_socket_receive, s); + net_socket_receive, NULL, s); snprintf(s->vc->info_str, sizeof(s->vc->info_str), "socket: fd=%d", fd); if (is_connected) { @@ -2218,6 +2907,28 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, 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, (char *)&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; @@ -2263,11 +2974,11 @@ static int net_socket_listen_init(VLANState *vlan, const char *host_str) perror("socket"); return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + socket_set_nonblock(fd); /* allow fast reuse */ val = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { @@ -2288,7 +2999,7 @@ static int net_socket_listen_init(VLANState *vlan, const char *host_str) static int net_socket_connect_init(VLANState *vlan, const char *host_str) { NetSocketState *s; - int fd, connected, ret; + int fd, connected, ret, err; struct sockaddr_in saddr; if (parse_host_port(&saddr, host_str) < 0) @@ -2299,18 +3010,19 @@ static int net_socket_connect_init(VLANState *vlan, const char *host_str) perror("socket"); return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + socket_set_nonblock(fd); connected = 0; for(;;) { ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) { - } else if (errno == EINPROGRESS) { + err = socket_error(); + if (err == EINTR || err == EWOULDBLOCK) { + } else if (err == EINPROGRESS) { break; } else { perror("connect"); - close(fd); + closesocket(fd); return -1; } } else { @@ -2327,7 +3039,32 @@ static int net_socket_connect_init(VLANState *vlan, const char *host_str) return 0; } -#endif /* !_WIN32 */ +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; + +} static int get_param_value(char *buf, int buf_size, const char *tag, const char *str) @@ -2420,6 +3157,9 @@ int net_client_init(const char *str) return -1; } } + if (get_param_value(buf, sizeof(buf), "model", p)) { + nd->model = strdup(buf); + } nd->vlan = vlan; nb_nics++; ret = 0; @@ -2431,10 +3171,22 @@ int net_client_init(const char *str) } else #ifdef CONFIG_SLIRP if (!strcmp(device, "user")) { + if (get_param_value(buf, sizeof(buf), "hostname", p)) { + pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf); + } ret = net_slirp_init(vlan); } else #endif -#ifndef _WIN32 +#ifdef _WIN32 + if (!strcmp(device, "tap")) { + char ifname[64]; + if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) { + fprintf(stderr, "tap: no interface name\n"); + return -1; + } + ret = tap_win32_init(vlan, ifname); + } else +#else if (!strcmp(device, "tap")) { char ifname[64]; char setup_script[1024]; @@ -2452,6 +3204,7 @@ int net_client_init(const char *str) ret = net_tap_init(vlan, ifname, setup_script); } } else +#endif if (!strcmp(device, "socket")) { if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { int fd; @@ -2463,12 +3216,13 @@ int net_client_init(const char *str) 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; @@ -2518,6 +3272,10 @@ static int usb_device_add(const char *devname) dev = usb_mouse_init(); if (!dev) return -1; + } else if (!strcmp(devname, "tablet")) { + dev = usb_tablet_init(); + if (!dev) + return -1; } else { return -1; } @@ -2656,7 +3414,7 @@ static void dumb_resize(DisplayState *ds, int w, int h) static void dumb_refresh(DisplayState *ds) { - vga_update_display(); + vga_hw_update(); } void dumb_display_init(DisplayState *ds) @@ -2719,6 +3477,7 @@ int qemu_set_fd_handler2(int fd, break; if (ioh->fd == fd) { *pioh = ioh->next; + qemu_free(ioh); break; } pioh = &ioh->next; @@ -2752,6 +3511,43 @@ int qemu_set_fd_handler(int fd, } /***********************************************************/ +/* Polling handling */ + +typedef struct PollingEntry { + PollingFunc *func; + void *opaque; + struct PollingEntry *next; +} PollingEntry; + +static PollingEntry *first_polling_entry; + +int qemu_add_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + pe = qemu_mallocz(sizeof(PollingEntry)); + if (!pe) + return -1; + pe->func = func; + pe->opaque = opaque; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); + *ppe = pe; + return 0; +} + +void qemu_del_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { + pe = *ppe; + if (pe->func == func && pe->opaque == opaque) { + *ppe = pe->next; + qemu_free(pe); + break; + } + } +} + +/***********************************************************/ /* savevm/loadvm support */ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) @@ -2970,9 +3766,6 @@ int qemu_loadvm(const char *filename) goto the_end; } for(;;) { -#if defined (DO_TB_FLUSH) - tb_flush(global_env); -#endif len = qemu_get_byte(f); if (feof(f)) break; @@ -3358,6 +4151,19 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) 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 @@ -3583,115 +4389,165 @@ void qemu_system_reset(void) void qemu_system_reset_request(void) { reset_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_shutdown_request(void) { shutdown_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_powerdown_request(void) { powerdown_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); -} - -static void main_cpu_reset(void *opaque) -{ -#if defined(TARGET_I386) || defined(TARGET_SPARC) - CPUState *env = opaque; - cpu_reset(env); -#endif + 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; + fd_set rfds, wfds, xfds; + int ret, nfds; + struct timeval tv; + PollingEntry *pe; + + /* XXX: need to suppress polling by better using win32 events */ + ret = 0; + for(pe = first_polling_entry; pe != NULL; pe = pe->next) { + ret |= pe->func(pe->opaque); + } #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; + if (ret == 0 && timeout > 0) { + int err; + HANDLE hEvents[1]; + + hEvents[0] = host_alarm; + ret = WaitForMultipleObjects(1, hEvents, FALSE, timeout); + switch(ret) { + case WAIT_OBJECT_0 + 0: + break; + case WAIT_TIMEOUT: + break; + default: + err = GetLastError(); + fprintf(stderr, "Wait error %d %d\n", ret, err); + break; } - ioh->ufd = pf; - pf++; + } +#endif + /* poll any events */ + /* XXX: separate device handlers from system ones */ + nfds = -1; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { + if (ioh->fd_read && + (!ioh->fd_read_poll || + ioh->fd_read_poll(ioh->opaque) != 0)) { + FD_SET(ioh->fd, &rfds); + if (ioh->fd > nfds) + nfds = ioh->fd; } - - 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); - } + if (ioh->fd_write) { + FD_SET(ioh->fd, &wfds); + if (ioh->fd > nfds) + nfds = ioh->fd; + } + } + + tv.tv_sec = 0; +#ifdef _WIN32 + tv.tv_usec = 0; +#else + tv.tv_usec = timeout * 1000; +#endif +#if defined(CONFIG_SLIRP) + if (slirp_inited) { + slirp_select_fill(&nfds, &rfds, &wfds, &xfds); + } +#endif + ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); + if (ret > 0) { + /* XXX: better handling of removal */ + for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { + ioh_next = ioh->next; + if (FD_ISSET(ioh->fd, &rfds)) { + ioh->fd_read(ioh->opaque); + } + if (FD_ISSET(ioh->fd, &wfds)) { + 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; + if (slirp_inited) { + if (ret < 0) { 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); - } } + slirp_select_poll(&rfds, &wfds, &xfds); + } +#endif +#ifdef _WIN32 + tap_win32_poll(); #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)); + 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 = global_env; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + CPUState *env; + cur_cpu = first_cpu; for(;;) { if (vm_running) { - ret = cpu_exec(env); + + env = cur_cpu; + for(;;) { + /* get next cpu */ + env = env->next_cpu; + if (!env) + env = first_cpu; +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + ret = cpu_exec(env); +#ifdef CONFIG_PROFILER + qemu_time += profile_getclock() - ti; +#endif + 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; @@ -3718,7 +4574,13 @@ int main_loop(void) } else { timeout = 10; } +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif main_loop_wait(timeout); +#ifdef CONFIG_PROFILER + dev_time += profile_getclock() - ti; +#endif } cpu_disable_ticks(); return ret; @@ -3740,16 +4602,17 @@ void help(void) "-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 cars\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" + " use -soundhw all to enable all of them\n" #endif "-localtime set the real time clock to local time [default=utc]\n" "-full-screen start in full screen\n" @@ -3763,20 +4626,26 @@ void help(void) #endif "\n" "Network options:\n" - "-net nic[,vlan=n][,macaddr=addr]\n" + "-net nic[,vlan=n][,macaddr=addr][,model=type]\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" + "-net user[,vlan=n][,hostname=host]\n" + " connect the user mode network stack to VLAN 'n' and send\n" + " hostname 'host' to DHCP clients\n" #endif -#ifndef _WIN32 +#ifdef _WIN32 + "-net tap[,vlan=n],ifname=name\n" + " connect the host TAP network interface to VLAN 'n'\n" +#else "-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=x][,listen=[host]:port][,connect=host:port]\n" - " connect the vlan 'n' to another VLAN using a socket connection\n" #endif + "-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" "-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" @@ -3807,6 +4676,7 @@ void help(void) " 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 + "-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n" "-no-kqemu disable KQEMU kernel module usage\n" #endif #ifdef USE_CODE_COPY @@ -3815,8 +4685,10 @@ void help(void) #ifdef TARGET_I386 "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n" " (default is CL-GD5446 PCI VGA)\n" + "-no-acpi disable ACPI\n" #endif "-loadvm file start right away with a saved state (loadvm in monitor)\n" + "-vnc display start a VNC server on display\n" "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -3863,7 +4735,6 @@ enum { QEMU_OPTION_m, QEMU_OPTION_nographic, #ifdef HAS_AUDIO - QEMU_OPTION_enable_audio, QEMU_OPTION_audio_help, QEMU_OPTION_soundhw, #endif @@ -3896,9 +4767,13 @@ enum { QEMU_OPTION_full_screen, QEMU_OPTION_pidfile, QEMU_OPTION_no_kqemu, + QEMU_OPTION_kernel_kqemu, QEMU_OPTION_win2k_hack, QEMU_OPTION_usb, QEMU_OPTION_usbdevice, + QEMU_OPTION_smp, + QEMU_OPTION_vnc, + QEMU_OPTION_no_acpi, }; typedef struct QEMUOption { @@ -3924,7 +4799,6 @@ const QEMUOption qemu_options[] = { { "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 @@ -3951,6 +4825,7 @@ const QEMUOption qemu_options[] = { { "no-code-copy", 0, QEMU_OPTION_no_code_copy }, #ifdef USE_KQEMU { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, + { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu }, #endif #if defined(TARGET_PPC) || defined(TARGET_SPARC) { "g", 1, QEMU_OPTION_g }, @@ -3965,10 +4840,13 @@ const QEMUOption qemu_options[] = { { "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 }, + { "vnc", HAS_ARG, QEMU_OPTION_vnc }, /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, + { "no-acpi", 0, QEMU_OPTION_no_acpi }, { NULL }, }; @@ -4036,62 +4914,112 @@ void register_machines(void) #else qemu_register_machine(&sun4m_machine); #endif +#elif defined(TARGET_ARM) + qemu_register_machine(&integratorcp926_machine); + qemu_register_machine(&integratorcp1026_machine); + qemu_register_machine(&versatilepb_machine); + qemu_register_machine(&versatileab_machine); +#elif defined(TARGET_SH4) + qemu_register_machine(&shix_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"); +struct soundhw soundhw[] = { +#ifdef TARGET_I386 + { + "pcspk", + "PC speaker", + 0, + 1, + { .init_isa = pcspk_audio_init } + }, +#endif + { + "sb16", + "Creative Sound Blaster 16", + 0, + 1, + { .init_isa = SB16_init } + }, + #ifdef CONFIG_ADLIB + { + "adlib", #ifdef HAS_YMF262 - printf ("adlib Yamaha YMF262 (OPL3)\n"); + "Yamaha YMF262 (OPL3)", #else - printf ("adlib Yamaha YM3812 (OPL2)\n"); + "Yamaha YM3812 (OPL2)", #endif + 0, + 1, + { .init_isa = Adlib_init } + }, #endif + #ifdef CONFIG_GUS - printf ("gus Gravis Ultrasound GF1\n"); + { + "gus", + "Gravis Ultrasound GF1", + 0, + 1, + { .init_isa = GUS_init } + }, #endif - printf ("es1370 ENSONIQ AudioPCI ES1370\n"); + + { + "es1370", + "ENSONIQ AudioPCI ES1370", + 0, + 0, + { .init_pci = es1370_init } + }, + + { NULL, NULL, 0, 0, { NULL } } +}; + +static void select_soundhw (const char *optarg) +{ + struct soundhw *c; + + if (*optarg == '?') { + show_valid_cards: + + printf ("Valid sound card names (comma separated):\n"); + for (c = soundhw; c->name; ++c) { + printf ("%-11s %s\n", c->name, c->descr); + } + printf ("\n-soundhw all will enable all of the above\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; + size_t l; const char *p; char *e; int bad_card = 0; - p = optarg; - tablen = sizeof (soundhw_tab) / sizeof (soundhw_tab[0]); + if (!strcmp (optarg, "all")) { + for (c = soundhw; c->name; ++c) { + c->enabled = 1; + } + return; + } + p = optarg; 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; + + for (c = soundhw; c->name; ++c) { + if (!strncmp (c->name, p, l)) { + c->enabled = 1; break; } } - if (i == tablen) { + + if (!c->name) { if (l > 80) { fprintf (stderr, "Unknown sound card name (too big to show)\n"); @@ -4120,7 +5048,6 @@ int main(int argc, char **argv) #endif int i, cdrom_index; int snapshot, linux_boot; - CPUState *env; const char *initrd_filename; const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; const char *kernel_filename, *kernel_cmdline; @@ -4353,13 +5280,6 @@ int main(int argc, char **argv) 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); @@ -4496,6 +5416,9 @@ int main(int argc, char **argv) case QEMU_OPTION_no_kqemu: kqemu_allowed = 0; break; + case QEMU_OPTION_kernel_kqemu: + kqemu_allowed = 2; + break; #endif case QEMU_OPTION_usb: usb_enabled = 1; @@ -4511,10 +5434,31 @@ int main(int argc, char **argv) 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; + case QEMU_OPTION_vnc: + vnc_display = atoi(optarg); + if (vnc_display < 0) { + fprintf(stderr, "Invalid VNC display\n"); + exit(1); + } + break; + case QEMU_OPTION_no_acpi: + acpi_enabled = 0; + break; } } } +#ifdef USE_KQEMU + if (smp_cpus > 1) + kqemu_allowed = 0; +#endif linux_boot = (kernel_filename != NULL); if (!linux_boot && @@ -4541,6 +5485,10 @@ int main(int argc, char **argv) setvbuf(stdout, NULL, _IOLBF, 0); #endif +#ifdef _WIN32 + socket_init(); +#endif + /* init network clients */ if (nb_net_clients == 0) { /* if no clients, we use a default config */ @@ -4659,15 +5607,8 @@ int main(int argc, char **argv) } } - /* init CPU state */ - env = cpu_init(); - global_env = env; - cpu_single_env = env; - - register_savevm("timer", 0, 1, timer_save, timer_load, env); - register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + register_savevm("timer", 0, 1, timer_save, timer_load, NULL); register_savevm("ram", 0, 1, ram_save, ram_load, NULL); - qemu_register_reset(main_cpu_reset, global_env); init_ioports(); cpu_calibrate_ticks(); @@ -4675,6 +5616,8 @@ int main(int argc, char **argv) /* terminal init */ if (nographic) { dumb_display_init(ds); + } else if (vnc_display != -1) { + vnc_display_init(ds, vnc_display); } else { #if defined(CONFIG_SDL) sdl_display_init(ds, full_screen); @@ -4685,8 +5628,6 @@ int main(int argc, char **argv) #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);