/* * Emulator State Information * * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. All rights reserved. * * Contact: * SeokYeon Hwang * MunKyu Im * GiWoong Kim * YeongKyoon Lee * HyunJun Son * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributors: * - S-Core Co., Ltd * */ #include "emul_state.h" #include #include "emulator_common.h" #if defined(CONFIG_LINUX) #include extern bool kvm_allowed; #elif defined(CONFIG_WIN32) #include extern bool hax_allowed; #elif defined(CONFIG_DARWIN) #include extern bool hax_allowed; #endif #include "qemu/osdep.h" #include "ui/console.h" #include "sysemu/sysemu.h" #include "block/block_int.h" #include "sysemu/block-backend.h" #include "net/net.h" #include "qmp-commands.h" #ifdef CONFIG_VIRTFS #include "fsdev/qemu-fsdev.h" #endif #include "emulator.h" #include "emulator_options.h" #include "hw/virtio/maru_virtio_input.h" #include "hw/virtio/maru_virtio_evdi.h" #include "util/net_helper.h" #include "util/new_debug_ch.h" DECLARE_DEBUG_CHANNEL(emul_state); #define MAXLEN 512 static EmulatorConfigInfo _emul_info; static EmulatorConfigState _emul_state; int get_max_touch_point(void) { return virtio_touchscreen_get_max_touch_point(); } /* current emulator condition */ int get_emulator_condition(void) { return _emul_state.emulator_condition; } void set_emulator_condition(int state) { if (state == BOOT_COMPLETED) { LOG_INFO("boot completed!\n"); // TODO: } else if (state == RESET) { LOG_INFO("reset emulator!\n"); } _emul_state.emulator_condition = state; } void set_emuld_connection(bool connected) { _emul_state.emuld_connection = connected; } bool get_emuld_connection(void) { return _emul_state.emuld_connection; } void set_sdb_connection(bool connected) { _emul_state.sdb_connection = connected; } bool get_sdb_connection(void) { return _emul_state.sdb_connection; } /* emualtor skin path */ const char* get_emul_skin_path(void) { if (!_emul_info.skin_path) { _emul_info.skin_path = get_variable("skin_path"); } return _emul_info.skin_path; } /* CPU virtualization */ bool get_emul_cpu_accel(void) { #ifdef CONFIG_LINUX return kvm_allowed; #else return hax_allowed; #endif return false; } // // cleaned-up // // launch_conf_path // only set by emulator.c when start up const char *launch_conf_file = NULL; // bin path // only set by osutil-{OS}.c when start up const char *bin_path = NULL; const char *get_bin_path(void) { if (bin_path) { return bin_path; } return ""; } // emulator kernel log file static const char *kernel_log_redirect_file = NULL; const char *get_kernel_log_redirect_file(void) { // TODO: kernel log path should be aquired from char device. if (kernel_log_redirect_file) { return kernel_log_redirect_file; } const char *vms_path = get_variable("vms_path"); const char *vm_name = get_variable("vm_name"); if (!vms_path || !vm_name) { // we can not specify log path. // emulator may not be launched from emulator-manager. kernel_log_redirect_file = g_strdup(""); } else { kernel_log_redirect_file = g_strdup_printf("%s/%s/logs/emulator.klog", vms_path, vm_name); } return kernel_log_redirect_file; } // emulator log file static const char *log_redirect_file = NULL; #ifdef CONFIG_WIN32 // for checking Windows version extern OSVERSIONINFO osvi; #endif const char *get_log_redirect_file(void) { int result = -1; char log_filename[PATH_MAX]; // should we compare stdout(fd/1) and stderr(fd/2) ?? #if defined(CONFIG_LINUX) result = readlink("/proc/self/fd/1", log_filename, PATH_MAX); if (result >= 0 && result < PATH_MAX) { log_filename[result] = '\0'; } #elif defined(CONFIG_DARWIN) result = fcntl(STDOUT_FILENO, F_GETPATH, log_filename); #elif defined(CONFIG_WIN32) # if WINVER >= 0x600 // works only vista or newer... if (osvi.dwMajorVersion >= 6) { char win32_log_filename_normalized[PATH_MAX]; HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD ret = GetFinalPathNameByHandle(handle_stdout, win32_log_filename_normalized, PATH_MAX, FILE_NAME_NORMALIZED); // strip "\\?\" if (ret > 0) { g_stpcpy(log_filename, win32_log_filename_normalized + 4); result = 0; } else { result = -1; } } # endif #endif if (log_redirect_file) { g_free((void *)log_redirect_file); } if (result >= 0) { log_redirect_file = g_strdup(log_filename); } else { // fail safe LOG_WARNING("Can not identify log redirection path, " "We should assume it !!!\n"); const char *vms_path = get_variable("vms_path"); const char *vm_name = get_variable("vm_name"); if (!vms_path || !vm_name) { // we can not specify log path. // emulator may not be launched from emulator-manager. log_redirect_file = g_strdup(""); } else { log_redirect_file = g_strdup_printf("%s/%s/logs/emulator.log", vms_path, vm_name); } } return log_redirect_file; } // drive image file static const char *drive_image_file = NULL; const char *get_drive_image_file(void) { if (drive_image_file) { return drive_image_file; } // we should parse from "drive" parameter. // so qemu_main() had to be called before. BlockBackend *bb = blk_by_name("drive"); if (bb) { BlockDriverState *bs = blk_bs(bb); drive_image_file = g_strdup(bs->filename); return drive_image_file; } // called before device initialized // or very weired situation LOG_WARNING("Can not identify main drive image file !!!\n"); return ""; } const char *get_drive_image_ver(void) { return get_variable("image_ver"); } // swap image file static const char *swap_image_file = NULL; const char *get_swap_image_file(void) { if (swap_image_file) { return swap_image_file; } // we should parse from "swap" parameter. // so qemu_main() had to be called before. BlockBackend *bb = blk_by_name("swap"); if (bb) { BlockDriverState *bs = blk_bs(bb); swap_image_file = g_strdup(bs->filename); return swap_image_file; } // called before device initialized // or very weired situation LOG_WARNING("Can not identify swap image file !!!\n"); return ""; } // kernel file const char *get_kernel_file(void) { return qemu_opt_get(qemu_get_machine_opts(), "kernel"); } // https proxy static const char *https_proxy_addr = NULL; const char *get_https_proxy_addr(void) { if (https_proxy_addr) { return https_proxy_addr; } const char *kernel_cmdline = qemu_opt_get(qemu_get_machine_opts(), "append"); // kernel cmdline always contains proxy information char *buf = g_strstr_len(kernel_cmdline, -1, "https_proxy="); if (buf) { char **token = g_strsplit_set(buf, "= ", 3); if (token[0] && token[1] && g_strcmp0(token[1], "")) { https_proxy_addr = g_strdup(token[1]); g_strfreev(token); LOG_INFO("HTTPS proxy address: %s\n", https_proxy_addr); return https_proxy_addr; } g_strfreev(token); } https_proxy_addr = g_strdup(""); LOG_INFO("HTTPS proxy address is not set.\n"); return https_proxy_addr; } // vm_name static const char *vm_name = NULL; const char *get_vm_name(void) { if (vm_name) { return vm_name; } // check variable const char *var_vm_name = get_variable("vm_name"); // check drive image name char *drive_file_vm_name = g_path_get_basename(get_drive_image_file()); // remove file extension char *last_dot = g_strrstr(drive_file_vm_name, "."); if (last_dot) { *last_dot = '\0'; } // if it has "emulimg-" prefix - it may be made by emulator-manager // we should remove it int start_index = 0; if (!strncmp("emulimg-", drive_file_vm_name, 8)) { start_index = 8; } if (g_strcmp0(var_vm_name, drive_file_vm_name + start_index)) { // when drive_file_vm_name != var_vm_name // we should warn to users LOG_WARNING("vm_name and image_file_name is not matched\n"); } // we choose drive_file_vm_name when var_vm_name is not provided if (!var_vm_name || strlen(var_vm_name) == 0) { vm_name = g_strdup(drive_file_vm_name + start_index); } else { vm_name = g_strdup(var_vm_name); } g_free(drive_file_vm_name); LOG_INFO("VM name: %s\n", vm_name); return vm_name; } const char *get_profile_name(void) { return get_variable("profile"); } const char *get_platform_version(void) { return get_variable("platform_version"); } #ifdef CONFIG_VIRTFS // host directory sharing path const char* get_host_directory_sharing_path(void) { FsDriverEntry *sharing_entry = get_fsdev_fsentry((char*)DEFAULT_STATIC_HDS_ID); const char *sharing_path = (sharing_entry != NULL ? sharing_entry->path : NULL); return sharing_path; } #else const char* get_host_directory_sharing_path(void) { return NULL; } #endif // GPU virtualization static bool gpu_accel_enabled; bool is_gpu_accel_enabled(void) { static bool is_done; PciInfoList *info_list, *info; Error *err = NULL; if (is_done) { return gpu_accel_enabled; } info_list = qmp_query_pci(&err); if (err) { LOG_WARNING("PCI devices not supported\n"); error_free(err); is_done = true; return false; } for (info = info_list; info; info = info->next) { PciDeviceInfoList *dev; for (dev = info->value->devices; dev; dev = dev->next) { /* TODO: use defines in the pci_regs.h instead of the hard coding */ if (dev->value->id->vendor == 0x19B1 && dev->value->id->device == 0x1010) { gpu_accel_enabled = true; } } } qapi_free_PciInfoList(info_list); is_done = true; return gpu_accel_enabled; } // ram size uint64_t get_ram_size(void) { return ram_size; } // VM base port // only set by net_helper.c when start up int vm_base_port = -1; int get_vm_base_port(void) { // should not be called before base port is prepared g_assert(vm_base_port != 0); return vm_base_port; } int get_vm_spice_port(void) { // should not be called before base port is prepared g_assert(vm_base_port != 0); return vm_base_port + SPICE_TCP_INDEX; } int get_vm_device_serial_number(void) { // should not be called before base port is prepared g_assert(vm_base_port != 0); if (is_netclient_tap_attached()) { return (START_VM_BASE_PORT + SDB_TCP_INDEX); } return vm_base_port + SDB_TCP_INDEX; } // look-up PCI devices bool is_pci_device_enabled(int device_id) { bool is_enabled = false; PciInfoList *info_list, *info; Error *err = NULL; info_list = qmp_query_pci(&err); if (err) { LOG_WARNING("PCI devices not supported\n"); error_free(err); return false; } for (info = info_list; info; info = info->next) { PciDeviceInfoList *dev; for (dev = info->value->devices; dev; dev = dev->next) { if (dev->value->id->device == device_id) { is_enabled = true; break; } } } qapi_free_PciInfoList(info_list); return is_enabled; } // mouse input bool is_mouse_enabled(void) { /* TODO: If the maru mouse device is added, * the device id should to be changed for it. */ return !is_pci_device_enabled(PCI_DEVICE_ID_VIRTIO_MARU_TOUCHSCREEN); } // touchscreen input bool is_touchscreen_enabled(void) { return is_pci_device_enabled(PCI_DEVICE_ID_VIRTIO_MARU_TOUCHSCREEN); } // tap attached bool is_netclient_tap_attached(void) { NetClientState *ncs[MAX_QUEUE_NUM]; int queues, i; queues = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < queues; ++i) { if (ncs[i]->info->type == NET_CLIENT_DRIVER_TAP) { return true; } } return false; } // vm_data_path static const char *vm_data_path = NULL; const char* get_vm_data_path(void) { if (vm_data_path) { return vm_data_path; } // check drive image file path char *drive_image_path = g_path_get_dirname(get_drive_image_file()); // check launch conf file path char *conf_file_path = g_path_get_dirname(launch_conf_file); if (g_strcmp0(drive_image_path, conf_file_path)) { // when drive_image_path != launch_conf_path // we should warn to users LOG_WARNING("drive_image_path and launch_conf_path is not matched\n"); } vm_data_path = drive_image_path; g_free(conf_file_path); LOG_INFO("VM data path: %s\n", vm_data_path); return vm_data_path; } static int compare_version(const char* version1, const char* version2) { int res = 0; char *ver1 = strdup(version1); char *ver2 = strdup(version2); char *remove1 = ver1; char *remove2 = ver2; char *next1 = ver1; char *next2 = ver2; while (*next1 != '\0' && *next2 != '\0') { long num1 = strtol(ver1, &next1, 10); long num2 = strtol(ver2, &next2, 10); res = num1 - num2; if (res) { break; } if (*next1 == '\0' || *next2 == '\0') { res = *next1 - *next2; break; } ver1 = next1 + 1; ver2 = next2 + 1; } free(remove1); free(remove2); return res; } const char *get_sdcard_image_path(void) { const gchar *sdcard_rel_path; const char *vm_data_path = get_vm_data_path(); const char *vm_platform_version = get_platform_version(); // Since tizen-3.0, vfat SDCard images are used instead of ext4. (+6 for "tizen-") if (compare_version(vm_platform_version + 6, "3.0") >= 0) { #ifndef CONFIG_WIN32 sdcard_rel_path = g_strdup_printf("%s/../../sdcard/vfat/", vm_data_path); #else sdcard_rel_path = g_strdup_printf("%s\\..\\..\\sdcard\\vfat\\", vm_data_path); #endif } else { #ifndef CONFIG_WIN32 sdcard_rel_path = g_strdup_printf("%s/../../sdcard/", vm_data_path); #else sdcard_rel_path = g_strdup_printf("%s\\..\\..\\sdcard\\", vm_data_path); #endif } char sdcard_abs_path[PATH_MAX + 1]; char *ptr = realpath(sdcard_rel_path, sdcard_abs_path); if (!ptr) { LOG_WARNING("Fail to get absolute path !!!\n"); return sdcard_rel_path; } #ifndef CONFIG_WIN32 const char *sdcard_path = g_strdup_printf("%s/", sdcard_abs_path); #else const char *sdcard_path; int path_length = strnlen(sdcard_abs_path, sizeof(sdcard_abs_path)); if (sdcard_abs_path[path_length - 1] != '\\') { sdcard_path = g_strdup_printf("%s\\", sdcard_abs_path); } else { sdcard_path = g_strdup(sdcard_abs_path); } #endif g_free((void*)sdcard_rel_path); LOG_INFO("SD card path: %s\n", sdcard_path); return sdcard_path; } // guest IP static const char *guest_ip = NULL; void set_guest_ip(const char *ip) { if (guest_ip) { LOG_WARNING("Guest IP is aleady set !!!\n"); return; } guest_ip = g_strdup(ip); } const char* get_guest_ip(void) { if (!guest_ip) { LOG_WARNING("Guest IP is NULL !!!\n"); } return guest_ip; } // host IP static const char *host_ip = NULL; const char* get_host_ip(void) { if (host_ip) { return host_ip; } // try to get from conf const char *host_ip_var = get_variable("host_ip"); if (host_ip_var) { host_ip = g_strdup(host_ip_var); return host_ip; } // failsafe for legacy // try to parse from kernel commandline const char *kernel_cmdline = qemu_opt_get(qemu_get_machine_opts(), "append"); char *buf = g_strstr_len(kernel_cmdline, -1, "host_ip="); char **splitted; if (buf && (splitted = g_strsplit(buf, " ", 2))) { if (splitted[0]) { host_ip = g_strdup(splitted[0]); } g_strfreev(splitted); return host_ip; } g_assert(host_ip != NULL); return NULL; } // display resolution // only set by vl.c when start up int initial_resolution_width = -1; int initial_resolution_height = -1; void set_initial_display_resolution(int width, int height) { LOG_INFO("initial display resolution: %dx%d\n", width, height); initial_resolution_width = width; initial_resolution_height = height; } int get_display_resolution_width(void) { return qemu_console_get_width(NULL, initial_resolution_width); } int get_display_resolution_height(void) { return qemu_console_get_height(NULL, initial_resolution_height); } // guest default home static const char *platform_default_home = NULL; const char* get_platform_default_home(void) { return platform_default_home; } void set_platform_default_home(const char *path) { if (!platform_default_home) { platform_default_home = g_strdup(path); } else { LOG_INFO("platform home path is already set : %s\n", platform_default_home); } } static void emul_state_notify_exit(Notifier *notifier, void *data) { g_free((void *)launch_conf_file); launch_conf_file = NULL; g_free((void *)bin_path); bin_path = NULL; g_free((void *)kernel_log_redirect_file); kernel_log_redirect_file = NULL; g_free((void *)log_redirect_file); log_redirect_file = NULL; g_free((void *)drive_image_file); drive_image_file = NULL; g_free((void *)swap_image_file); swap_image_file = NULL; g_free((void *)https_proxy_addr); https_proxy_addr = NULL; g_free((void *)vm_name); vm_name = NULL; g_free((void *)vm_data_path); vm_data_path = NULL; g_free((void *)guest_ip); guest_ip = NULL; g_free((void *)host_ip); host_ip = NULL; g_free((void *)platform_default_home); platform_default_home = NULL; } static Notifier emul_state_exit = { .notify = emul_state_notify_exit }; void init_emul_state(void) { emulator_add_exit_notifier(&emul_state_exit); }