| /* |
| * QEMU System Emulator |
| * |
| * Copyright (c) 2003-2008 Fabrice Bellard |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| /* the following is needed on Linux to define ptsname() in stdlib.h */ |
| #if defined(__linux__) |
| #define _GNU_SOURCE 1 |
| #endif |
| |
| #include "qemu-common.h" |
| #include "net.h" |
| #include "console.h" |
| #include "qemu-timer.h" |
| #include "qemu-char.h" |
| #include "block.h" |
| #include "sockets.h" |
| #include "audio/audio.h" |
| |
| #include "android/android.h" |
| #include "charpipe.h" |
| #include "android/globals.h" |
| #include "android/utils/bufprint.h" |
| #include "android/utils/system.h" |
| #include "android/core-connection.h" |
| #include "android/framebuffer-ui.h" |
| |
| #ifdef CONFIG_MEMCHECK |
| #include "memcheck/memcheck.h" |
| #endif // CONFIG_MEMCHECK |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <sys/time.h> |
| #include <zlib.h> |
| |
| /* Needed early for CONFIG_BSD etc. */ |
| #include "config-host.h" |
| |
| #ifndef _WIN32 |
| #include <libgen.h> |
| #include <pwd.h> |
| #include <sys/times.h> |
| #include <sys/wait.h> |
| #include <termios.h> |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| #include <sys/resource.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <net/if.h> |
| #if defined(__NetBSD__) |
| #include <net/if_tap.h> |
| #endif |
| #ifdef __linux__ |
| #include <linux/if_tun.h> |
| #endif |
| #include <arpa/inet.h> |
| #include <dirent.h> |
| #include <netdb.h> |
| #include <sys/select.h> |
| #ifdef CONFIG_BSD |
| #include <sys/stat.h> |
| #if defined(__FreeBSD__) || defined(__DragonFly__) |
| #include <libutil.h> |
| #else |
| #include <util.h> |
| #endif |
| #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) |
| #include <freebsd/stdlib.h> |
| #else |
| #ifdef __linux__ |
| #include <pty.h> |
| #include <malloc.h> |
| #include <linux/rtc.h> |
| |
| /* For the benefit of older linux systems which don't supply it, |
| we use a local copy of hpet.h. */ |
| /* #include <linux/hpet.h> */ |
| #include "hpet.h" |
| |
| #include <linux/ppdev.h> |
| #include <linux/parport.h> |
| #endif |
| #ifdef __sun__ |
| #include <sys/stat.h> |
| #include <sys/ethernet.h> |
| #include <sys/sockio.h> |
| #include <netinet/arp.h> |
| #include <netinet/in.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #include <netinet/ip_icmp.h> // must come after ip.h |
| #include <netinet/udp.h> |
| #include <netinet/tcp.h> |
| #include <net/if.h> |
| #include <syslog.h> |
| #include <stropts.h> |
| #endif |
| #endif |
| #endif |
| |
| #if defined(__OpenBSD__) |
| #include <util.h> |
| #endif |
| |
| #if defined(CONFIG_VDE) |
| #include <libvdeplug.h> |
| #endif |
| |
| #ifdef _WIN32 |
| #include <windows.h> |
| #include <malloc.h> |
| #include <sys/timeb.h> |
| #include <mmsystem.h> |
| #define getopt_long_only getopt_long |
| #define memalign(align, size) malloc(size) |
| #endif |
| |
| |
| #ifdef CONFIG_COCOA |
| #undef main |
| #define main qemu_main |
| #endif /* CONFIG_COCOA */ |
| |
| #include "qemu-timer.h" |
| #include "qemu-char.h" |
| |
| #if defined(CONFIG_SKINS) && !defined(CONFIG_STANDALONE_CORE) |
| #undef main |
| #define main qemu_main |
| #endif |
| |
| #include "qemu_socket.h" |
| |
| static const char *data_dir; |
| |
| static DisplayState *display_state; |
| DisplayType display_type = DT_DEFAULT; |
| const char* keyboard_layout = NULL; |
| int64_t ticks_per_sec; |
| int vm_running; |
| static int autostart; |
| QEMUClock *rtc_clock; |
| #ifdef TARGET_SPARC |
| int graphic_width = 1024; |
| int graphic_height = 768; |
| int graphic_depth = 8; |
| #else |
| int graphic_width = 800; |
| int graphic_height = 600; |
| int graphic_depth = 15; |
| #endif |
| static int full_screen = 0; |
| #ifdef CONFIG_SDL |
| static int no_frame = 0; |
| #endif |
| int no_quit = 0; |
| int smp_cpus = 1; |
| const char *vnc_display; |
| int acpi_enabled = 1; |
| int no_reboot = 0; |
| int no_shutdown = 0; |
| int cursor_hide = 1; |
| int graphic_rotate = 0; |
| #ifndef _WIN32 |
| int daemonize = 0; |
| #endif |
| #ifdef TARGET_ARM |
| int old_param = 0; |
| #endif |
| const char *qemu_name; |
| int alt_grab = 0; |
| |
| static QEMUTimer *nographic_timer; |
| |
| uint8_t qemu_uuid[16]; |
| |
| extern int android_display_width; |
| extern int android_display_height; |
| extern int android_display_bpp; |
| |
| extern void dprint( const char* format, ... ); |
| |
| /* Instance of the "attach UI" Emulator's core console client. */ |
| extern CoreConnection* attach_client; |
| /* Instance of the "framebuffer" console client. */ |
| extern ClientFramebuffer* fb_client; |
| |
| #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) |
| |
| /* 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 HOST_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; |
| } |
| |
| /***********************************************************/ |
| /* I/O handling */ |
| |
| typedef struct IOHandlerRecord { |
| int fd; |
| IOCanReadHandler *fd_read_poll; |
| IOHandler *fd_read; |
| IOHandler *fd_write; |
| int deleted; |
| 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, |
| IOCanReadHandler *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) { |
| ioh->deleted = 1; |
| break; |
| } |
| pioh = &ioh->next; |
| } |
| } else { |
| for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { |
| if (ioh->fd == fd) |
| goto found; |
| } |
| ANEW0(ioh); |
| 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; |
| ioh->deleted = 0; |
| } |
| 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); |
| } |
| |
| /***********************************************************/ |
| /* main execution loop */ |
| |
| static void gui_update(void *opaque) |
| { |
| uint64_t interval = GUI_REFRESH_INTERVAL; |
| DisplayState *ds = opaque; |
| DisplayChangeListener *dcl = ds->listeners; |
| |
| dpy_refresh(ds); |
| |
| while (dcl != NULL) { |
| if (dcl->gui_timer_interval && |
| dcl->gui_timer_interval < interval) |
| interval = dcl->gui_timer_interval; |
| dcl = dcl->next; |
| } |
| qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock)); |
| } |
| |
| static void nographic_update(void *opaque) |
| { |
| uint64_t interval = GUI_REFRESH_INTERVAL; |
| |
| qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); |
| } |
| |
| static int shutdown_requested = 0; |
| |
| void qemu_system_shutdown_request(void) |
| { |
| shutdown_requested = 1; |
| } |
| |
| |
| |
| static int qemu_event_init(void) |
| { |
| return 0; |
| } |
| |
| static int qemu_init_main_loop(void) |
| { |
| return qemu_event_init(); |
| } |
| |
| #define qemu_mutex_lock_iothread() do { } while (0) |
| #define qemu_mutex_unlock_iothread() do { } while (0) |
| |
| |
| #ifdef _WIN32 |
| static void host_main_loop_wait(int *timeout) |
| { |
| } |
| #else |
| static void host_main_loop_wait(int *timeout) |
| { |
| } |
| #endif |
| |
| void main_loop_wait(int timeout) |
| { |
| IOHandlerRecord *ioh; |
| fd_set rfds, wfds, xfds; |
| int ret, nfds; |
| struct timeval tv; |
| |
| host_main_loop_wait(&timeout); |
| |
| /* 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->deleted) |
| continue; |
| 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; |
| } |
| if (ioh->fd_write) { |
| FD_SET(ioh->fd, &wfds); |
| if (ioh->fd > nfds) |
| nfds = ioh->fd; |
| } |
| } |
| |
| tv.tv_sec = timeout / 1000; |
| tv.tv_usec = (timeout % 1000) * 1000; |
| |
| ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); |
| if (ret > 0) { |
| IOHandlerRecord **pioh; |
| |
| for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { |
| if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { |
| ioh->fd_read(ioh->opaque); |
| } |
| if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { |
| ioh->fd_write(ioh->opaque); |
| } |
| } |
| |
| /* remove deleted IO handlers */ |
| pioh = &first_io_handler; |
| while (*pioh) { |
| ioh = *pioh; |
| if (ioh->deleted) { |
| *pioh = ioh->next; |
| AFREE(ioh); |
| } else |
| pioh = &ioh->next; |
| } |
| } |
| |
| qemu_run_all_timers(); |
| } |
| |
| static void main_loop(void) |
| { |
| while (!shutdown_requested) { |
| main_loop_wait(qemu_calculate_timeout()); |
| } |
| } |
| |
| #ifndef _WIN32 |
| |
| static void termsig_handler(int signal) |
| { |
| /* qemu_system_shutdown_request(); */ |
| } |
| |
| static void sigchld_handler(int signal) |
| { |
| waitpid(-1, NULL, WNOHANG); |
| } |
| |
| static void sighandler_setup(void) |
| { |
| struct sigaction act; |
| |
| memset(&act, 0, sizeof(act)); |
| act.sa_handler = termsig_handler; |
| sigaction(SIGINT, &act, NULL); |
| sigaction(SIGHUP, &act, NULL); |
| sigaction(SIGTERM, &act, NULL); |
| |
| act.sa_handler = sigchld_handler; |
| act.sa_flags = SA_NOCLDSTOP; |
| sigaction(SIGCHLD, &act, NULL); |
| } |
| |
| #endif |
| |
| #define QEMU_FILE_TYPE_BIOS 0 |
| #define QEMU_FILE_TYPE_KEYMAP 1 |
| |
| char *qemu_find_file(int type, const char *name) |
| { |
| int len; |
| const char *subdir; |
| char *buf; |
| |
| /* If name contains path separators then try it as a straight path. */ |
| if ((strchr(name, '/') || strchr(name, '\\')) |
| && access(name, R_OK) == 0) { |
| return strdup(name); |
| } |
| switch (type) { |
| case QEMU_FILE_TYPE_BIOS: |
| subdir = ""; |
| break; |
| case QEMU_FILE_TYPE_KEYMAP: |
| subdir = "keymaps/"; |
| break; |
| default: |
| abort(); |
| } |
| len = strlen(data_dir) + strlen(name) + strlen(subdir) + 2; |
| buf = android_alloc0(len); |
| snprintf(buf, len, "%s/%s%s", data_dir, subdir, name); |
| if (access(buf, R_OK)) { |
| AFREE(buf); |
| return NULL; |
| } |
| return buf; |
| } |
| |
| #ifdef _WIN32 |
| static BOOL WINAPI qemu_ctrl_handler(DWORD type) |
| { |
| exit(STATUS_CONTROL_C_EXIT); |
| return TRUE; |
| } |
| #endif |
| |
| int main(int argc, char **argv, char **envp) |
| { |
| DisplayState *ds; |
| DisplayChangeListener *dcl; |
| |
| init_clocks(); |
| |
| #ifndef _WIN32 |
| { |
| struct sigaction act; |
| sigfillset(&act.sa_mask); |
| act.sa_flags = 0; |
| act.sa_handler = SIG_IGN; |
| sigaction(SIGPIPE, &act, NULL); |
| } |
| #else |
| SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); |
| /* Note: cpu_interrupt() is currently not SMP safe, so we force |
| QEMU to run on a single CPU */ |
| { |
| HANDLE h; |
| DWORD mask, smask; |
| int i; |
| h = GetCurrentProcess(); |
| if (GetProcessAffinityMask(h, &mask, &smask)) { |
| for(i = 0; i < 32; i++) { |
| if (mask & (1 << i)) |
| break; |
| } |
| if (i != 32) { |
| mask = 1 << i; |
| SetProcessAffinityMask(h, mask); |
| } |
| } |
| } |
| #endif |
| |
| autostart= 1; |
| |
| if (qemu_init_main_loop()) { |
| fprintf(stderr, "qemu_init_main_loop failed\n"); |
| exit(1); |
| } |
| |
| if (init_timer_alarm() < 0) { |
| fprintf(stderr, "could not initialize alarm timer\n"); |
| exit(1); |
| } |
| |
| #ifdef _WIN32 |
| socket_init(); |
| #endif |
| |
| #ifndef _WIN32 |
| /* must be after terminal init, SDL library changes signal handlers */ |
| sighandler_setup(); |
| #endif |
| |
| /* just use the first displaystate for the moment */ |
| ds = display_state = get_displaystate(); |
| |
| if (display_type == DT_DEFAULT) { |
| display_type = DT_SDL; |
| } |
| |
| |
| switch (display_type) { |
| case DT_NOGRAPHIC: |
| break; |
| case DT_SDL: |
| sdl_display_init(ds, full_screen, no_frame); |
| break; |
| default: |
| break; |
| } |
| dpy_resize(ds); |
| |
| dcl = ds->listeners; |
| while (dcl != NULL) { |
| if (dcl->dpy_refresh != NULL) { |
| ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds); |
| qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock)); |
| } |
| dcl = dcl->next; |
| } |
| |
| if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) { |
| nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL); |
| qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock)); |
| } |
| |
| //qemu_chr_initial_reset(); |
| |
| main_loop(); |
| |
| if (attach_client != NULL) { |
| core_connection_detach(attach_client); |
| core_connection_close(attach_client); |
| core_connection_free(attach_client); |
| } |
| |
| if (fb_client != NULL) { |
| clientfb_destroy(fb_client); |
| fb_client = NULL; |
| } |
| |
| quit_timers(); |
| return 0; |
| } |