diff options
Diffstat (limited to 'arch_init.c')
-rw-r--r-- | arch_init.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/arch_init.c b/arch_init.c new file mode 100644 index 0000000000..171e21c7de --- /dev/null +++ b/arch_init.c @@ -0,0 +1,509 @@ +/* + * 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. + */ +#include <stdint.h> +#include <stdarg.h> +#ifndef _WIN32 +#include <sys/mman.h> +#endif +#include "config.h" +#include "monitor.h" +#include "sysemu.h" +#include "arch_init.h" +#include "audio/audio.h" +#include "hw/pc.h" +#include "hw/pci.h" +#include "hw/audiodev.h" +#include "kvm.h" +#include "migration.h" +#include "net.h" +#include "gdbstub.h" +#include "hw/smbios.h" + +#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 + +const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf"; + +#if defined(TARGET_ALPHA) +#define QEMU_ARCH QEMU_ARCH_ALPHA +#elif defined(TARGET_ARM) +#define QEMU_ARCH QEMU_ARCH_ARM +#elif defined(TARGET_CRIS) +#define QEMU_ARCH QEMU_ARCH_CRIS +#elif defined(TARGET_I386) +#define QEMU_ARCH QEMU_ARCH_I386 +#elif defined(TARGET_M68K) +#define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_MICROBLAZE) +#define QEMU_ARCH QEMU_ARCH_MICROBLAZE +#elif defined(TARGET_MIPS) +#define QEMU_ARCH QEMU_ARCH_MIPS +#elif defined(TARGET_PPC) +#define QEMU_ARCH QEMU_ARCH_PPC +#elif defined(TARGET_S390X) +#define QEMU_ARCH QEMU_ARCH_S390X +#elif defined(TARGET_SH4) +#define QEMU_ARCH QEMU_ARCH_SH4 +#elif defined(TARGET_SPARC) +#define QEMU_ARCH QEMU_ARCH_SPARC +#endif + +const uint32_t arch_type = QEMU_ARCH; + +/***********************************************************/ +/* ram save/restore */ + +#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +#define RAM_SAVE_FLAG_COMPRESS 0x02 +#define RAM_SAVE_FLAG_MEM_SIZE 0x04 +#define RAM_SAVE_FLAG_PAGE 0x08 +#define RAM_SAVE_FLAG_EOS 0x10 + +static int is_dup_page(uint8_t *page, uint8_t ch) +{ + uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; + uint32_t *array = (uint32_t *)page; + int i; + + for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { + if (array[i] != val) { + return 0; + } + } + + return 1; +} + +static int ram_save_block(QEMUFile *f) +{ + static ram_addr_t current_addr = 0; + ram_addr_t saved_addr = current_addr; + ram_addr_t addr = 0; + int found = 0; + + while (addr < last_ram_offset) { + if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { + uint8_t *p; + + cpu_physical_memory_reset_dirty(current_addr, + current_addr + TARGET_PAGE_SIZE, + MIGRATION_DIRTY_FLAG); + + p = qemu_get_ram_ptr(current_addr); + + if (is_dup_page(p, *p)) { + qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_COMPRESS); + qemu_put_byte(f, *p); + } else { + qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_PAGE); + qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + } + + found = 1; + break; + } + addr += TARGET_PAGE_SIZE; + current_addr = (saved_addr + addr) % last_ram_offset; + } + + return found; +} + +static uint64_t bytes_transferred; + +static ram_addr_t ram_save_remaining(void) +{ + ram_addr_t addr; + ram_addr_t count = 0; + + for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { + count++; + } + } + + return count; +} + +uint64_t ram_bytes_remaining(void) +{ + return ram_save_remaining() * TARGET_PAGE_SIZE; +} + +uint64_t ram_bytes_transferred(void) +{ + return bytes_transferred; +} + +uint64_t ram_bytes_total(void) +{ + return last_ram_offset; +} + +int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) +{ + ram_addr_t addr; + uint64_t bytes_transferred_last; + double bwidth = 0; + uint64_t expected_time = 0; + + if (stage < 0) { + cpu_physical_memory_set_dirty_tracking(0); + return 0; + } + + if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { + qemu_file_set_error(f); + return 0; + } + + if (stage == 1) { + bytes_transferred = 0; + + /* Make sure all dirty bits are set */ + for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { + if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { + cpu_physical_memory_set_dirty(addr); + } + } + + /* Enable dirty memory tracking */ + cpu_physical_memory_set_dirty_tracking(1); + + qemu_put_be64(f, last_ram_offset | RAM_SAVE_FLAG_MEM_SIZE); + } + + bytes_transferred_last = bytes_transferred; + bwidth = qemu_get_clock_ns(rt_clock); + + while (!qemu_file_rate_limit(f)) { + int ret; + + ret = ram_save_block(f); + bytes_transferred += ret * TARGET_PAGE_SIZE; + if (ret == 0) { /* no more blocks */ + break; + } + } + + bwidth = qemu_get_clock_ns(rt_clock) - bwidth; + bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; + + /* if we haven't transferred anything this round, force expected_time to a + * a very high value, but without crashing */ + if (bwidth == 0) { + bwidth = 0.000001; + } + + /* try transferring iterative blocks of memory */ + if (stage == 3) { + /* flush all remaining blocks regardless of rate limiting */ + while (ram_save_block(f) != 0) { + bytes_transferred += TARGET_PAGE_SIZE; + } + cpu_physical_memory_set_dirty_tracking(0); + } + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; + + return (stage == 2) && (expected_time <= migrate_max_downtime()); +} + +int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + ram_addr_t addr; + int flags; + + if (version_id != 3) { + return -EINVAL; + } + + do { + addr = qemu_get_be64(f); + + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + if (flags & RAM_SAVE_FLAG_MEM_SIZE) { + if (addr != last_ram_offset) { + return -EINVAL; + } + } + + if (flags & RAM_SAVE_FLAG_COMPRESS) { + uint8_t ch = qemu_get_byte(f); + memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE); +#ifndef _WIN32 + if (ch == 0 && + (!kvm_enabled() || kvm_has_sync_mmu())) { + madvise(qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE, + MADV_DONTNEED); + } +#endif + } else if (flags & RAM_SAVE_FLAG_PAGE) { + qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE); + } + if (qemu_file_has_error(f)) { + return -EIO; + } + } while (!(flags & RAM_SAVE_FLAG_EOS)); + + return 0; +} + +void qemu_service_io(void) +{ + qemu_notify_event(); +} + +#ifdef HAS_AUDIO +struct soundhw soundhw[] = { +#ifdef HAS_AUDIO_CHOICE +#if defined(TARGET_I386) || defined(TARGET_MIPS) + { + "pcspk", + "PC speaker", + 0, + 1, + { .init_isa = pcspk_audio_init } + }, +#endif + +#ifdef CONFIG_SB16 + { + "sb16", + "Creative Sound Blaster 16", + 0, + 1, + { .init_isa = SB16_init } + }, +#endif + +#ifdef CONFIG_CS4231A + { + "cs4231a", + "CS4231A", + 0, + 1, + { .init_isa = cs4231a_init } + }, +#endif + +#ifdef CONFIG_ADLIB + { + "adlib", +#ifdef HAS_YMF262 + "Yamaha YMF262 (OPL3)", +#else + "Yamaha YM3812 (OPL2)", +#endif + 0, + 1, + { .init_isa = Adlib_init } + }, +#endif + +#ifdef CONFIG_GUS + { + "gus", + "Gravis Ultrasound GF1", + 0, + 1, + { .init_isa = GUS_init } + }, +#endif + +#ifdef CONFIG_AC97 + { + "ac97", + "Intel 82801AA AC97 Audio", + 0, + 0, + { .init_pci = ac97_init } + }, +#endif + +#ifdef CONFIG_ES1370 + { + "es1370", + "ENSONIQ AudioPCI ES1370", + 0, + 0, + { .init_pci = es1370_init } + }, +#endif + +#endif /* HAS_AUDIO_CHOICE */ + + { NULL, NULL, 0, 0, { NULL } } +}; + +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 { + size_t l; + const char *p; + char *e; + int bad_card = 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 (c = soundhw; c->name; ++c) { + if (!strncmp(c->name, p, l) && !c->name[l]) { + c->enabled = 1; + break; + } + } + + if (!c->name) { + 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; + } + } +} +#else +void select_soundhw(const char *optarg) +{ +} +#endif + +int qemu_uuid_parse(const char *str, uint8_t *uuid) +{ + int ret; + + if (strlen(str) != 36) { + return -1; + } + + ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], + &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], + &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], + &uuid[15]); + + if (ret != 16) { + return -1; + } +#ifdef TARGET_I386 + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); +#endif + return 0; +} + +void do_acpitable_option(const char *optarg) +{ +#ifdef TARGET_I386 + if (acpi_table_add(optarg) < 0) { + fprintf(stderr, "Wrong acpi table provided\n"); + exit(1); + } +#endif +} + +void do_smbios_option(const char *optarg) +{ +#ifdef TARGET_I386 + if (smbios_entry_add(optarg) < 0) { + fprintf(stderr, "Wrong smbios provided\n"); + exit(1); + } +#endif +} + +void cpudef_init(void) +{ +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file */ +#endif +} + +int audio_available(void) +{ +#ifdef HAS_AUDIO + return 1; +#else + return 0; +#endif +} + +int kvm_available(void) +{ +#ifdef CONFIG_KVM + return 1; +#else + return 0; +#endif +} + +int xen_available(void) +{ +#ifdef CONFIG_XEN + return 1; +#else + return 0; +#endif +} |